From f9b0ae0030f77e70e62138e6caa7e000ef233551 Mon Sep 17 00:00:00 2001 From: NoEngineerHere Date: Tue, 16 Jul 2024 02:18:02 +1000 Subject: [PATCH] Add files via upload Added "halfnut" functionality the ability to set thread start and stop points, along with ui elements to indicate status HOLD sync button at desired shoulder, then jog to desired start point and PRESS sync to set start point. Then, sync can be PRESSED to jog between thread start and stop points. This only happens when the leadscrew is positioned between the set start and stop points to prevent a runaway saddle. Once thread stop point is set (start point is optional, just saves jogging everytime. (To do: jog button jogs on hold, stops on release. Have added the ability to restaret thread from any leadscrew position (I think!))) hit that NUT button and watch it go. It will thread up until the thread stop point, then you can retract your tool (todo: add auto cross slide support) PRESS SYNC to jog back to the thread start point, and do it all again. I'm going to bed. --- TeensyELS2.31/README | 9 + TeensyELS2.31/include/README | 39 ++ TeensyELS2.31/lib/README | 46 ++ TeensyELS2.31/platformio.ini | 19 + TeensyELS2.31/src/config.h | 19 + TeensyELS2.31/src/display.cpp | 154 +++++++ TeensyELS2.31/src/display.h | 47 ++ TeensyELS2.31/src/feedSymbol.h | 23 + TeensyELS2.31/src/lockedSymbol.h | 10 + TeensyELS2.31/src/main.cpp | 717 +++++++++++++++++++++++++++++ TeensyELS2.31/src/pauseSymbol.h | 10 + TeensyELS2.31/src/position.cpp | 0 TeensyELS2.31/src/position.h | 11 + TeensyELS2.31/src/runSymbol.h | 10 + TeensyELS2.31/src/threadSymbol.h | 24 + TeensyELS2.31/src/unlockedSymbol.h | 9 + TeensyELS2.31/test/README | 11 + 17 files changed, 1158 insertions(+) create mode 100644 TeensyELS2.31/README create mode 100644 TeensyELS2.31/include/README create mode 100644 TeensyELS2.31/lib/README create mode 100644 TeensyELS2.31/platformio.ini create mode 100644 TeensyELS2.31/src/config.h create mode 100644 TeensyELS2.31/src/display.cpp create mode 100644 TeensyELS2.31/src/display.h create mode 100644 TeensyELS2.31/src/feedSymbol.h create mode 100644 TeensyELS2.31/src/lockedSymbol.h create mode 100644 TeensyELS2.31/src/main.cpp create mode 100644 TeensyELS2.31/src/pauseSymbol.h create mode 100644 TeensyELS2.31/src/position.cpp create mode 100644 TeensyELS2.31/src/position.h create mode 100644 TeensyELS2.31/src/runSymbol.h create mode 100644 TeensyELS2.31/src/threadSymbol.h create mode 100644 TeensyELS2.31/src/unlockedSymbol.h create mode 100644 TeensyELS2.31/test/README 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