diff --git a/TeensyELS2.31/README b/TeensyELS2.31/README new file mode 100644 index 0000000..d6cc888 --- /dev/null +++ b/TeensyELS2.31/README @@ -0,0 +1,9 @@ +# Teensy Electronic leadscrew +This is an implementation of an electronic leadscrew using a Teensy microcrontroller. + +# Architecture +We use [https://platformio.org/](platformio) as the infrastructure to build this project. + +# Installation +You will currently need to setup platformio to build this, it can also handle installation to your teensy as well. +NOTE: you will have to modify some of the configuration to get this set up for your particular lathe, instructions coming soon. \ No newline at end of file diff --git a/TeensyELS2.31/include/README b/TeensyELS2.31/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/TeensyELS2.31/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/TeensyELS2.31/lib/README b/TeensyELS2.31/lib/README new file mode 100644 index 0000000..2593a33 --- /dev/null +++ b/TeensyELS2.31/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/TeensyELS2.31/platformio.ini b/TeensyELS2.31/platformio.ini new file mode 100644 index 0000000..4c0c808 --- /dev/null +++ b/TeensyELS2.31/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:teensy41] +platform = teensy +board = teensy41 +framework = arduino + +lib_deps = + jsware/AbleButtons@^0.4.0 + adafruit/Adafruit GFX Library@^1.11.9 + adafruit/Adafruit SSD1306@^2.5.10 diff --git a/TeensyELS2.31/src/config.h b/TeensyELS2.31/src/config.h new file mode 100644 index 0000000..c2e457a --- /dev/null +++ b/TeensyELS2.31/src/config.h @@ -0,0 +1,19 @@ +// This file contains all hardware configs for your system + +#pragma once + +/** + * Display + * + * This setting allows you to select what type of display you want to use. + * The selection will hopefully grow as time goes on! + * + * Options: + * SSD1306_128_64: 128x64 oled + */ +#define ELS_DISPLAY SSD1306_128_64 + +#if ELS_DISPLAY == SSD1306_128_64 +// define this if you have a dedicated pin for the oled reset +#define PIN_DISPLAY_RESET -1 +#endif diff --git a/TeensyELS2.31/src/display.cpp b/TeensyELS2.31/src/display.cpp new file mode 100644 index 0000000..c5342cd --- /dev/null +++ b/TeensyELS2.31/src/display.cpp @@ -0,0 +1,154 @@ +#include "display.h" + +// Images +#include "feedSymbol.h" +#include "lockedSymbol.h" +#include "pauseSymbol.h" +#include "runSymbol.h" +#include "threadSymbol.h" +#include "unlockedSymbol.h" + +void Display::init() { +#ifdef ELS_DISPLAY == SSD1306_128_64 + if (!this->m_ssd1306.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { + Serial.println(F("SSD1306 allocation failed")); + for (;;); + } + m_ssd1306.clearDisplay(); +#endif +} + +void Display::update(boolean driveMode, float pitch, boolean lock, + boolean enabled, boolean jogging, boolean syncState, boolean startPointSet) { +#if ELS_DISPLAY == SSD1306_128_64 + m_ssd1306.clearDisplay(); +#endif + + this->drawMode(driveMode); + this->drawPitch(pitch); + this->drawLocked(lock); + this->drawEnabled(enabled); + this->drawSyncState(syncState); + this->drawThreadStartPointSet(startPointSet); + this->drawJogging(jogging); + + +#if ELS_DISPLAY == SSD1306_128_64 + m_ssd1306.display(); +#endif +} + +void Display::drawMode(boolean driveMode) { +#if ELS_DISPLAY == SSD1306_128_64 + if (driveMode == true) { + + m_ssd1306.drawBitmap(57, 32, threadSymbol, 64, 32, WHITE); + } else { + m_ssd1306.drawBitmap(57, 32, feedSymbol, 64, 32, WHITE); + } +#endif +} + +void Display::drawPitch(float pitch) { +#if ELS_DISPLAY == SSD1306_128_64 + m_ssd1306.setCursor(55, 6); + m_ssd1306.setTextSize(3); + m_ssd1306.setTextColor(WHITE); + m_ssd1306.print(pitch); +#endif +} + +void Display::drawEnabled(boolean enabled) { +#if ELS_DISPLAY == SSD1306_128_64 + m_ssd1306.fillRoundRect(26, 40, 20, 20, 2, WHITE); + + if (enabled == true) { + m_ssd1306.drawBitmap(28, 42, runSymbol, 16, 16, BLACK); + } else { + m_ssd1306.drawBitmap(28, 42, pauseSymbol, 16, 16, BLACK); + } +#endif +} + +void Display::drawLocked(boolean locked) { +#if ELS_DISPLAY == SSD1306_128_64 + m_ssd1306.fillRoundRect(2, 40, 20, 20, 2, WHITE); + + if (locked == true) { + m_ssd1306.drawBitmap(4, 42, lockedSymbol, 16, 16, BLACK); + } else { + m_ssd1306.drawBitmap(4, 42, unlockedSymbol, 16, 16, BLACK); + } +#endif +} + +void Display::drawJogging(boolean jogging) { +#if ELS_DISPLAY == SSD1306_128_64 + + + if (jogging == true) { + m_ssd1306.fillRoundRect(30, 14, 68, 36, 12, WHITE); + m_ssd1306.fillRoundRect(32, 16, 64, 32, 10, BLACK); + + m_ssd1306.setCursor(37, 22); + m_ssd1306.setTextSize(3); + m_ssd1306.setTextColor(WHITE); + m_ssd1306.print("JOG"); + } + + +#endif +} + +void Display::drawSyncState(boolean syncState) { +#if ELS_DISPLAY == SSD1306_128_64 + + + if (syncState == true) { + m_ssd1306.setCursor(2, 2); + m_ssd1306.setTextSize(2); + m_ssd1306.setTextColor(WHITE); + m_ssd1306.print("SYNC"); + } + + else + { + m_ssd1306.setCursor(2, 2); + m_ssd1306.setTextSize(2); + m_ssd1306.setTextColor(WHITE); + m_ssd1306.print("SYNC"); + m_ssd1306.fillRoundRect(0, 8, 50, 2, 1, WHITE); + } + +#endif +} + +void Display::drawThreadStartPointSet(boolean startPointSet) { +#if ELS_DISPLAY == SSD1306_128_64 + + +if (startPointSet == true) { + m_ssd1306.setCursor(2, 21); + m_ssd1306.setTextSize(2); + m_ssd1306.setTextColor(WHITE); + m_ssd1306.print("STRT"); +} + +else +{ + m_ssd1306.setCursor(2, 21); + m_ssd1306.setTextSize(2); + m_ssd1306.setTextColor(WHITE); + m_ssd1306.print("STRT"); + m_ssd1306.fillRoundRect(0, 27, 50, 2, 1, WHITE); + +} +#endif + +} + + + + + + diff --git a/TeensyELS2.31/src/display.h b/TeensyELS2.31/src/display.h new file mode 100644 index 0000000..eac0f1f --- /dev/null +++ b/TeensyELS2.31/src/display.h @@ -0,0 +1,47 @@ + +#include "config.h" + +#define SSD1306_128_64 0 + +#if ELS_DISPLAY == SSD1306_128_64 + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +// some displays can have different addresses, this is what we attempt to init +#define SCREEN_ADDRESS 0x3C + +#include +#include + +#else + +#error "Please choose a valid display. Refer to config.h for options" + +#endif + +class Display { + public: +#if ELS_DISPLAY == SSD1306_128_64 + Adafruit_SSD1306 m_ssd1306; +#endif + Display() { +#if ELS_DISPLAY == SSD1306_128_64 + this->m_ssd1306 = + Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, PIN_DISPLAY_RESET); +#endif + } + + void init(); + void update(boolean driveMode, float pitch, boolean lock, boolean enabled, boolean jogMode, boolean syncState, boolean startPointSet); + + protected: + void drawMode(boolean driveMode); + void drawPitch(float rate); + void drawEnabled(boolean enabled); + void drawLocked(boolean locked); + void drawJogging(boolean jogging); + void drawSyncState(boolean syncState); + void drawThreadStartPointSet(boolean startPointSet); + + +}; \ No newline at end of file diff --git a/TeensyELS2.31/src/feedSymbol.h b/TeensyELS2.31/src/feedSymbol.h new file mode 100644 index 0000000..855bc46 --- /dev/null +++ b/TeensyELS2.31/src/feedSymbol.h @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// File generated by LCD Assistant +// http://en.radzio.dxp.pl/bitmap_converter/ +//------------------------------------------------------------------------------ +static const uint8_t PROGMEM feedSymbol [] = { + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xE0, 0x06, 0x00, +0x00, 0x00, 0x00, 0x01, 0xF1, 0xF0, 0x07, 0x80, 0x00, 0x00, 0x00, 0x03, 0xE0, 0xF8, 0x07, 0xE0, +0x1F, 0xF0, 0x00, 0x07, 0xC0, 0x7F, 0xFF, 0xF8, 0x3F, 0xF8, 0x00, 0x0F, 0x80, 0x3F, 0xFF, 0xFC, +0x3F, 0xFC, 0x00, 0x1F, 0x00, 0x1F, 0xFF, 0xFC, 0x1F, 0xFE, 0x00, 0x3E, 0x00, 0x0F, 0xFF, 0xF8, +0x00, 0x1F, 0x00, 0x7C, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x0F, 0x80, 0xF8, 0x00, 0x00, 0x07, 0x80, +0x00, 0x07, 0xC1, 0xF0, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0xE3, 0xE0, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0xF7, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/TeensyELS2.31/src/lockedSymbol.h b/TeensyELS2.31/src/lockedSymbol.h new file mode 100644 index 0000000..b44f962 --- /dev/null +++ b/TeensyELS2.31/src/lockedSymbol.h @@ -0,0 +1,10 @@ +//------------------------------------------------------------------------------ +// File generated by LCD Assistant +// http://en.radzio.dxp.pl/bitmap_converter/ +//------------------------------------------------------------------------------ + +static const uint8_t PROGMEM lockedSymbol [] = { + +0x03, 0xC0, 0x0F, 0xF0, 0x1F, 0xF8, 0x3C, 0x3C, 0x38, 0x1C, 0x78, 0x1E, 0x7F, 0xFE, 0x7F, 0xFE, +0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE +}; diff --git a/TeensyELS2.31/src/main.cpp b/TeensyELS2.31/src/main.cpp new file mode 100644 index 0000000..0805016 --- /dev/null +++ b/TeensyELS2.31/src/main.cpp @@ -0,0 +1,717 @@ +// Libraries +#include +#include +#include + +#include "display.h" + +#define CW digitalWriteFast(3, HIGH); // direction output pin +#define CCW digitalWriteFast(3, LOW); // direction output pin + +using Button = AblePullupCallbackButton; // AblePullupButton + // Using basic pull-up resistor circuit. + +using ButtonList = AblePullupCallbackButtonList; // AblePullupButtonList; + // Using basic pull-up button list. + +void rateIncCall(Button::CALLBACK_EVENT, unsigned char); +void rateDecCall(Button::CALLBACK_EVENT, unsigned char); +void modeCycleCall(Button::CALLBACK_EVENT, unsigned char); +void threadSyncCall(Button::CALLBACK_EVENT, unsigned char); +void halfNutCall(Button::CALLBACK_EVENT, unsigned char); +void enaCall(Button::CALLBACK_EVENT, unsigned char); +void lockCall(Button::CALLBACK_EVENT, unsigned char); +void jogLeftCall(Button::CALLBACK_EVENT, unsigned char); +void jogRightCall(Button::CALLBACK_EVENT, unsigned char); + + +Button rateInc(4, rateIncCall); +Button rateDec(5, rateDecCall); +Button modeCycle(6, modeCycleCall); +Button threadSync(7, threadSyncCall); // thread sync (hold) and set/jog to start point (press) +Button halfNut(8, halfNutCall); +Button ena(9, enaCall); +Button lock(10, lockCall); +Button jogLeft(24, jogLeftCall); +Button jogRight(25, jogRightCall); + +Button *keys[] = {&rateInc, &rateDec, &modeCycle, &threadSync, &halfNut, + &ena, &lock, &jogLeft, &jogRight + +}; + +ButtonList keyPad(keys); +Button setHeldTime(100); + +Display display; + +int EncoderMatrix[16] = { + 0, -1, 1, 2, 1, 0, 2, -1, -1, + 2, 0, 1, 2, 1, -1, 0}; // encoder output matrix, output = X (old) * 4 + Y + // (new) + +volatile int oldPos; +volatile int newPos; + +const int pinA = 14; // encoder pin A +const int pinB = 15; // encoder pin B +const int stp = 2; // step output pin +const int dir = 3; // direction output pin +const int spindlePulsesPerRev = 2000; +const int leadscrewStepsPerRev = 2000; +double leadscrewPitch = 2.5; +int desiredThreadRevolutions = 1; + +const int safetyDelay = 500000; + + + + +volatile int pulseCount; +volatile int jogpulseCount; +volatile int jogdirectionIncrement; +volatile int pulseID; +volatile int spindlePulsePosition = 1; +volatile int spindleAngle; +volatile int leadscrewAngle; +volatile int leadscrewPulsePosition; +volatile int leadscrewAbsolutePosition; +volatile int lastDir; +volatile int threadStart; + +int feedSelect = 8; +int jogRate; +int jogStepTime = 100; + +long long lastPulse; +long long lastjogPulse; + +volatile bool jogging = false; +volatile bool jogLeftButtonPressed = false; +volatile bool jogRightButtonPressed = false; +volatile bool driveMode = true; // select threading mode (true) or feeding mode (false) +volatile bool enabled = false; +volatile bool lockState = false; +volatile bool readyToThread = false; +volatile bool synced = false; + +volatile bool threadStartSet = false; + + + + +// UI Values +const char gearLetter[3] = {65, 66, 67}; + +const float threadPitch[20] = {0.35, 0.40, 0.45, 0.50, 0.60, 0.70, 0.80, 1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 5.50, 6.00}; + +const float feedPitch[20] = {0.05, 0.08, 0.10, 0.12, 0.15, 0.18, 0.20, 0.23, 0.25, 0.28, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75}; + +// Ratio Values + +const int numeratorTable[20] = {7, 8, 9, 2, 12, 14, 16, 4, 1, 6, 7, 8, 2, 12, 14, 16, 18, 4, 22, 24}; // metric threading ratio tables + +const int denominatorTable[20] = {25, 25, 25, 5, 25, 25, 25, 5, 1, 5, 5, 5, 1, 5, 5, 5, 5, 1, 5, 5}; + +const int numeratorTable2[20] = {1, 3, 1, 3, 3, 7, 1, 9, 1, 11, 3, 7, 1, 9, 1, 11, 3, 13, 7, 3}; // feed ratio tables + +const int denominatorTable2[20] = {80, 160, 40, 100, 80, 160, 20, 160, 16, 160, 40, 80, 10, 80, 8, 80, 20, 80, 40, 16}; + +volatile int accumulator; +volatile int numerator = numeratorTable[feedSelect]; +volatile int denominator = denominatorTable[feedSelect]; + +void Achange(); +void Bchange(); +void bresenham(); +void spindleCount(); +void threadToShoulder(); +void goToThreadStart(); +void threadMode(); +void feedMode(); +void modeHandle(); +void jogToThreadStart(); + +bool checkSynchronization(int desiredSpindleAngle, int currentSpindlePosition, int currentLeadscrewPosition, int feedSelect, const int* numeratorTable, const int* denominatorTable); +int calculateDesiredSpindleAngle(int feedSelect, double leadscrewPitch, int desiredThreadRevolutions); +const int tolerance = 0; + +void setup() { + + Serial.begin(115200); + // Pinmodes + + pinMode(14, INPUT_PULLUP); // encoder pin 1 + pinMode(15, INPUT_PULLUP); // encoder pin 2 + pinMode(2, OUTPUT); // step output pin + pinMode(3, OUTPUT); // direction output pin + pinMode(4, INPUT_PULLUP); // rate Inc (press) + pinMode(5, INPUT_PULLUP); // rate Dec (press) + pinMode(6, INPUT_PULLUP); // mode cycle (press) + pinMode(7, INPUT_PULLUP); // thread sync (hold) and set/jog to start point (press) + pinMode(8, INPUT_PULLUP); // half nut (press?) + pinMode(9, INPUT_PULLUP); // enable toggle (press) + pinMode(10, INPUT_PULLUP); // lock toggle (hold) + pinMode(24, INPUT_PULLUP); // jog left (press) + pinMode(25, INPUT_PULLUP); // jog right (press) + + // Interupts + + attachInterrupt(digitalPinToInterrupt(pinA), Achange, CHANGE); // encoder channel A interrupt + attachInterrupt(digitalPinToInterrupt(pinB), Bchange, CHANGE); // encoder channel B interrupt + + // Display Initalisation + + + + display.init(); + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + +} + +void loop() { + + keyPad.handle(); + modeHandle(); + + + + if (!driveMode) // feed mode + { + if (enabled && pulseCount != 0) { // state 1, enabled + + bresenham(); + + } + + if(!enabled && pulseCount != 0) + { + pulseCount --; + } + } + + if (driveMode) //thread mode + { + + + + + if (enabled && pulseCount > 0) //if threadmode, enabled, run as normal + { + bresenham(); + } + + if (!enabled && pulseCount > 0) //if thread mode, disabled, count spindle encoder pulses + { + + + spindleCount(); + + int currentSpindlePosition = spindlePulsePosition; + int currentLeadscrewPosition = leadscrewAbsolutePosition; + + int desiredSpindleAngle = calculateDesiredSpindleAngle(feedSelect, leadscrewPitch, desiredThreadRevolutions); + + bool synchronised = checkSynchronization(desiredSpindleAngle, currentSpindlePosition, currentLeadscrewPosition, feedSelect, numeratorTable, denominatorTable); + + Serial.print("spindleAngle: "); + Serial.print(currentSpindlePosition); + Serial.print(" "); + Serial.print("desiredSpindleAngle: "); + Serial.print(desiredSpindleAngle); + Serial.print(" "); + Serial.print("synchronised: "); + Serial.print(synchronised ? "true" : "false"); + Serial.print(" "); + Serial.print("leadscrewAbsolutePosition: "); + Serial.print(leadscrewAbsolutePosition); + Serial.print("\r\n"); + + if (synchronised && readyToThread && synced) + { + enabled = true; + readyToThread = false; + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } + + + + } + + + + + if (jogging) //jogging active + { + + + + while (jogpulseCount != 0) + { + if (micros() - lastjogPulse > jogStepTime && jogpulseCount > 0) + { + CW; + digitalWriteFast(stp, HIGH); + delayMicroseconds(2); + digitalWriteFast(stp, LOW); + delayMicroseconds(2); + jogpulseCount --; + leadscrewPulsePosition ++; + leadscrewAbsolutePosition ++; + if (leadscrewPulsePosition == 2000) + { + leadscrewPulsePosition = 1; + } + lastjogPulse = micros(); + } + + else if (micros() - lastjogPulse > jogStepTime && jogpulseCount < 0) + { + CCW; + digitalWriteFast(stp, HIGH); + delayMicroseconds(2); + digitalWriteFast(stp, LOW); + delayMicroseconds(2); + jogpulseCount ++; + leadscrewPulsePosition --; + leadscrewAbsolutePosition --; + if (leadscrewPulsePosition == 0) + { + leadscrewPulsePosition = 2000; + } + lastjogPulse = micros(); + + } + + + + } + + jogging = false; + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + + + + } + + } + +} + + +int calculateDesiredSpindleAngle(int feedSelect, double leadscrewPitch, int desiredThreadRevolutions) { + // Calculate selected thread pitch in mm + double selectedThreadPitch = threadPitch[feedSelect]; + + // Calculate ratio from numerator and denominator tables + double ratio = (double) numeratorTable[feedSelect] / (double) denominatorTable[feedSelect]; + + // Calculate lead of the thread + double lead = selectedThreadPitch * ratio; + + // Calculate desired spindle angle in pulses + double totalAngleDegrees = (lead / leadscrewPitch) * 360.0 * desiredThreadRevolutions; + int desiredSpindleAngle = static_cast(totalAngleDegrees / 360.0 * spindlePulsesPerRev); + + return desiredSpindleAngle; +} + + +bool checkSynchronization(int desiredSpindleAngle, int currentSpindlePosition, int currentLeadscrewPosition, int feedSelect, const int* numeratorTable, const int* denominatorTable) { + // Calculate spindle angle based on encoder position + int spindleAngle = (currentSpindlePosition - 1) % spindlePulsesPerRev + 1; // Ensure within 1 to spindlePulsesPerRev range + + // Adjust desired spindle angle within one revolution + desiredSpindleAngle = desiredSpindleAngle % (spindlePulsesPerRev * denominatorTable[feedSelect] / numeratorTable[feedSelect]); + + // Calculate spindle movement adjusted by the ratio tables + double ratio = (double) numeratorTable[feedSelect] / (double) denominatorTable[feedSelect]; + int spindleMovement = static_cast((spindleAngle - 1) * ratio) + 1; // Adjust for zero-based indexing + + // Check if spindle movement matches the desired spindle angle within tolerance + if (abs(spindleMovement - desiredSpindleAngle) <= tolerance) { + return true; // Synchronized for reengagement + } + + return false; // Not synchronized +} + + + +bool needToJogToThreadStart() { + // Determine if leadscrew needs to be jogged to thread start point based on application logic + if (jogging) { + return false; // Already jogging, no need to jog again + } + + // Check if leadscrew position is within the range of 0 to threadStart + if (leadscrewAbsolutePosition >= 0 && leadscrewAbsolutePosition <= threadStart) { + return true; // Need to jog towards threadStart + } else if (leadscrewAbsolutePosition <= 0 && leadscrewAbsolutePosition >= threadStart) { + return true; // Need to jog towards threadStart + } else { + return false; // No need to jog + } +} + + + +void bresenham(){ //sends pulses to motor based on pitch ratio, carries remainders + + + + accumulator = numerator + accumulator; // "bresenham algorithm", carries remainder of required + // motor steps to next pulse received from spindle + + // change direction based on sign of the pulse count + + + while (accumulator >= + denominator) { // sends required motor steps to motor + + accumulator = accumulator - denominator; + + digitalWriteFast(stp, HIGH); + delayMicroseconds(2); + digitalWriteFast(stp, LOW); + delayMicroseconds(2); + int leadscrewIncrement = lastDir ? 1 : -1; + leadscrewAbsolutePosition += leadscrewIncrement; + if (synced && leadscrewAbsolutePosition == 0) //if thread shoulder has been established with "sync", disable at sync point + { + enabled = false; + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } + + } + + pulseCount --; + lastPulse = micros(); + + +} + + + +void jogToThreadStart() { + // Calculate jog pulse count based on direction (move towards 0 or towards threadStart) + if (leadscrewAbsolutePosition > 0) { + jogpulseCount = -leadscrewAbsolutePosition; // Move towards 0 from positive position + } else if (leadscrewAbsolutePosition < 0) { + jogpulseCount = -leadscrewAbsolutePosition; // Move towards 0 from negative position + } else { + jogpulseCount = threadStart; // Move towards threadStart from 0 position + } + + // Update jogging state + jogging = true; +} + +void spindleCount(){ //keeps track of spindle angle when disabled in thread mode ("thread dial indicator") + + if (lastDir == true) + { + spindlePulsePosition ++; + pulseCount --; + //if (spindlePulsePosition > 2000) + //{ + // spindlePulsePosition = 1; + //} + + } + + else if (lastDir == false) + { + spindlePulsePosition --; + pulseCount --; + + //if (spindlePulsePosition < 1) + //{ + // spindlePulsePosition = 2000; + //} + } + + + + +} + +void modeHandle(){ //sets pitch ratio based on feedSelect variable + + if (driveMode){ + + numerator = numeratorTable[feedSelect]; + denominator = denominatorTable[feedSelect]; + + } + + if (!driveMode){ + + numerator = numeratorTable2[feedSelect]; + denominator = denominatorTable2[feedSelect]; + + } + +} + +void Achange(){ // validates encoder pulses, adds to pulse variable + + + + oldPos = newPos; + bitWrite(newPos, 0, digitalReadFast(pinA)); + bitWrite(newPos, 1, digitalReadFast(pinB)); // adds A to B, converts to integer + pulseID = EncoderMatrix[(oldPos * 4) + newPos]; + + if (pulseID == 1) + { + CW + lastDir = true; + pulseCount ++; + } + + else if (pulseID == -1) + { + CCW + lastDir = false; + pulseCount ++; + } + + + + + + +} + +void Bchange(){ // validates encoder pulses, adds to pulse variable + + oldPos = newPos; + bitWrite(newPos, 0, digitalReadFast(pinA)); + bitWrite(newPos, 1, digitalReadFast(pinB)); // adds A to B, converts to integer + pulseID = EncoderMatrix[(oldPos * 4) + newPos]; // assigns value from encoder matrix to determine validity and direction of encoder pulse + + if (pulseID == 1) + { + CW + lastDir = true; + pulseCount ++; + } + + else if (pulseID == -1) + { + CCW + lastDir = false; + pulseCount ++; + } + + +} + +void rateIncCall(Button::CALLBACK_EVENT event, uint8_t) { // increases feedSelect variable on button press + + if (driveMode == false || ((micros() - lastPulse) > safetyDelay && lockState == false)) { + + if (event == Button::PRESSED_EVENT) + { + + if (feedSelect < 19) + { + feedSelect++; + } + + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + + } + } +} + +void rateDecCall(Button::CALLBACK_EVENT event, uint8_t) { // decreases feedSelect variable on button press + + if (driveMode == false || ((micros() - lastPulse) > safetyDelay && lockState == false)) { + + if (event == Button::PRESSED_EVENT) + { + if (feedSelect > 0) + { + feedSelect--; + } + + + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } + } +} + +void halfNutCall(Button::CALLBACK_EVENT event, uint8_t) { // sets readyToThread to true on button hold, starts threading + + if (event == Button::PRESSED_EVENT && driveMode && synced) + { + readyToThread = true; + Serial.println("Ready To Thread"); + } +} + +void enaCall(Button::CALLBACK_EVENT event, uint8_t) { // toggles enabled variable on button press + + if (event == Button::PRESSED_EVENT) { + if (enabled == true) { + enabled = false; + } + + else { + enabled = true; + } + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } +} + +void lockCall(Button::CALLBACK_EVENT event, uint8_t) { // toggles lockState variable on button press + + if (event == Button::HELD_EVENT) { + lockState = !lockState; + synced = false; + readyToThread = false; + threadStartSet = false; + enabled = false; + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } +} + +void threadSyncCall(Button::CALLBACK_EVENT event, uint8_t) { // toggles sync status on button hold, when synced, single press first sets start point, when start point is set, single press jogs to start point + + if (event == Button::HELD_EVENT) { + if (synced == false && driveMode) { + lockState = true; + leadscrewPulsePosition = 1; + leadscrewAbsolutePosition = 0; + spindlePulsePosition = 1; + synced = true; + } + + else { + synced = false; + threadStartSet = false; + } + + + + } + + if (event == Button::PRESSED_EVENT && synced == true && !jogging) { + if(!threadStartSet) + { + threadStart = leadscrewAbsolutePosition; + threadStartSet = true; + } + else if (!jogging && needToJogToThreadStart()) + { + jogToThreadStart(); + } + + + } + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + +} + +void modeCycleCall(Button::CALLBACK_EVENT event, uint8_t) { // toggles between thread / feed modes on button press + + if (event == Button::PRESSED_EVENT && (micros() - lastPulse) > safetyDelay && lockState == false) { + if (driveMode == false) { + driveMode = true; + } + + else { + driveMode = false; + synced = false; + } + + modeHandle(); + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } +} + +void jogLeftCall(Button::CALLBACK_EVENT event, uint8_t) { // when disabled in thread mode, a single press jogs one "thread" in the specified direction + + + + if (event == Button::PRESSED_EVENT) { + if (driveMode == true && enabled == false) { + jogging = true; + long unsigned int fullThreadRotation = 2000 * numerator / denominator; + jogpulseCount -= fullThreadRotation; + } + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } + + +} + +void jogRightCall(Button::CALLBACK_EVENT event, uint8_t) { // when disabled in thread mode, a single press jogs one "thread" in the specified direction + + if (event == Button::PRESSED_EVENT) { + if (driveMode == true && enabled == false) { + jogging = true; + long unsigned int fullThreadRotation = 2000 * numerator / denominator; + jogpulseCount += fullThreadRotation; + } + + display.update( + driveMode, + driveMode == true ? threadPitch[feedSelect] : feedPitch[feedSelect], + lockState, enabled, jogging, synced, threadStartSet); + } + +} + + + + + + + + + + diff --git a/TeensyELS2.31/src/pauseSymbol.h b/TeensyELS2.31/src/pauseSymbol.h new file mode 100644 index 0000000..c6f1355 --- /dev/null +++ b/TeensyELS2.31/src/pauseSymbol.h @@ -0,0 +1,10 @@ +//------------------------------------------------------------------------------ +// File generated by LCD Assistant +// http://en.radzio.dxp.pl/bitmap_converter/ +//------------------------------------------------------------------------------ + +static const uint8_t PROGMEM pauseSymbol [] = { + +0x78, 0x1E, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, +0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0x78, 0x1E +}; diff --git a/TeensyELS2.31/src/position.cpp b/TeensyELS2.31/src/position.cpp new file mode 100644 index 0000000..e69de29 diff --git a/TeensyELS2.31/src/position.h b/TeensyELS2.31/src/position.h new file mode 100644 index 0000000..aaf867a --- /dev/null +++ b/TeensyELS2.31/src/position.h @@ -0,0 +1,11 @@ + + +/** + * This class handles the position of the ELS + */ + +class ELSPosition { + enum Direction { LEFT, RIGHT }; + + +}; diff --git a/TeensyELS2.31/src/runSymbol.h b/TeensyELS2.31/src/runSymbol.h new file mode 100644 index 0000000..9471201 --- /dev/null +++ b/TeensyELS2.31/src/runSymbol.h @@ -0,0 +1,10 @@ +//------------------------------------------------------------------------------ +// File generated by LCD Assistant +// http://en.radzio.dxp.pl/bitmap_converter/ +//------------------------------------------------------------------------------ + +static const uint8_t PROGMEM runSymbol [] = { + +0xC0, 0x00, 0xF0, 0x00, 0xFC, 0x00, 0xFF, 0x00, 0xFF, 0xC0, 0xFF, 0xF0, 0xFF, 0xFC, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xF0, 0xFF, 0xC0, 0xFF, 0x00, 0xFC, 0x00, 0xF0, 0x00, 0xC0, 0x00 +}; diff --git a/TeensyELS2.31/src/threadSymbol.h b/TeensyELS2.31/src/threadSymbol.h new file mode 100644 index 0000000..d6d044c --- /dev/null +++ b/TeensyELS2.31/src/threadSymbol.h @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// File generated by LCD Assistant +// http://en.radzio.dxp.pl/bitmap_converter/ +//------------------------------------------------------------------------------ + +static const uint8_t PROGMEM threadSymbol [] = { + +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x03, 0xBB, 0xBB, 0xBB, 0xBB, 0x80, 0x1F, 0xE7, 0xFF, 0x77, 0x77, 0x77, 0x77, 0x60, +0x1F, 0xEF, 0xFF, 0x77, 0x77, 0x77, 0x77, 0x60, 0x1F, 0xEF, 0xFF, 0x77, 0x77, 0x77, 0x77, 0x60, +0x1F, 0xEF, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xE0, 0x1F, 0xEF, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xE0, +0x1F, 0xEF, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xE0, 0x1F, 0xEF, 0xFE, 0xEE, 0xEE, 0xEE, 0xEE, 0xE0, +0x1F, 0xEF, 0xFD, 0xDD, 0xDD, 0xDD, 0xDD, 0xE0, 0x1F, 0xEF, 0xFD, 0xDD, 0xDD, 0xDD, 0xDD, 0xE0, +0x1F, 0xE7, 0xFD, 0xDD, 0xDD, 0xDD, 0xDD, 0xE0, 0x00, 0x00, 0x03, 0xBB, 0xBB, 0xBB, 0xBB, 0xC0, +0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/TeensyELS2.31/src/unlockedSymbol.h b/TeensyELS2.31/src/unlockedSymbol.h new file mode 100644 index 0000000..1a3e368 --- /dev/null +++ b/TeensyELS2.31/src/unlockedSymbol.h @@ -0,0 +1,9 @@ +//------------------------------------------------------------------------------ +// File generated by LCD Assistant +// http://en.radzio.dxp.pl/bitmap_converter/ +//------------------------------------------------------------------------------ + +static const uint8_t PROGMEM unlockedSymbol [] = { +0x03, 0xC0, 0x0F, 0xF0, 0x1F, 0xF8, 0x3C, 0x3C, 0x38, 0x1C, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x0E, +0x00, 0x0E, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE +}; diff --git a/TeensyELS2.31/test/README b/TeensyELS2.31/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/TeensyELS2.31/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html