diff --git a/KiwiBoard.emf b/KiwiBoard.emf index d4946c9..1ddf44c 100644 --- a/KiwiBoard.emf +++ b/KiwiBoard.emf @@ -217,12 +217,12 @@ { "parentId": 12, "type": "analogItem", - "defaultValue": "0", + "defaultValue": "44", "item": { - "maxValue": 10, + "maxValue": 119, "offset": 1, "divisor": 1, - "unitName": "min", + "unitName": "sec", "step": 1, "name": "Time", "variableName": "spin_duration", @@ -372,6 +372,27 @@ "staticDataInRAM": false } }, + { + "parentId": 15, + "type": "analogItem", + "defaultValue": "4", + "item": { + "maxValue": 9, + "offset": 1, + "divisor": 1, + "unitName": "min", + "step": 1, + "name": "Preheat", + "variableName": "MaxPreheat", + "id": 44, + "eepromAddress": 94, + "functionName": "@settings_changed", + "readOnly": false, + "localOnly": false, + "visible": true, + "staticDataInRAM": false + } + }, { "parentId": 7, "type": "subMenu", @@ -447,19 +468,15 @@ }, { "parentId": 30, - "type": "analogItem", - "defaultValue": "1", + "type": "boolItem", + "defaultValue": "true", "item": { - "maxValue": 2000, - "offset": 0, - "divisor": 1, - "unitName": "", - "step": 5, - "name": "PWM Threshold ", - "variableName": "stealthTransition", - "id": 42, - "eepromAddress": 92, - "functionName": "@stealthTransitionChanged", + "naming": "ON_OFF", + "name": "StealthChop", + "variableName": "StealthChop", + "id": 45, + "eepromAddress": 96, + "functionName": "@stealthChopChange", "readOnly": false, "localOnly": false, "visible": true, @@ -487,7 +504,7 @@ "codeOptions": { "embeddedPlatform": "ARDUINO32", "lastDisplayUuid": "e6342680-6fab-4d1b-ac08-ce8dbfc0974c", - "lastInputUuid": "6dd87e7d-e751-4912-a70d-91793b1d3d87", + "lastInputUuid": "89cd7f70-0457-4884-97c2-0db904ccb0ba", "lastRemoteUuid": "2c101fec-1f7d-4ff3-8d2b-992ad41e7fcb", "lastRemoteUuids": [ "2c101fec-1f7d-4ff3-8d2b-992ad41e7fcb" @@ -576,51 +593,6 @@ "latestValue": "400", "subsystem": "DISPLAY" }, - { - "name": "SW_POLLING_MODE", - "latestValue": "SWITCHES_NO_POLLING", - "subsystem": "INPUT" - }, - { - "name": "SWITCH_IODEVICE", - "latestValue": "devicePins", - "subsystem": "INPUT" - }, - { - "name": "ENCODER_PIN_A", - "latestValue": "ENC1", - "subsystem": "INPUT" - }, - { - "name": "ENCODER_PIN_B", - "latestValue": "ENC2", - "subsystem": "INPUT" - }, - { - "name": "ENCODER_PIN_OK", - "latestValue": "BUTTON", - "subsystem": "INPUT" - }, - { - "name": "ENCODER_BACK_PIN", - "latestValue": "-1", - "subsystem": "INPUT" - }, - { - "name": "ENCODER_NEXT_PIN", - "latestValue": "-1", - "subsystem": "INPUT" - }, - { - "name": "PULLUP_LOGIC", - "latestValue": "true", - "subsystem": "INPUT" - }, - { - "name": "ENCODER_IS_QUARTER_CYCLE", - "latestValue": "false", - "subsystem": "INPUT" - }, { "name": "ITEM_FONT", "latestValue": "num:,4", diff --git a/TMC_calculations.xlsx b/TMC_calculations.xlsx index 15c1c32..4369004 100644 Binary files a/TMC_calculations.xlsx and b/TMC_calculations.xlsx differ diff --git a/images/Stop.png b/images/Stop.png new file mode 100644 index 0000000..580db2e Binary files /dev/null and b/images/Stop.png differ diff --git a/images/blank-widget-50x50.png b/images/blank-widget-50x50.png new file mode 100644 index 0000000..dc649d5 Binary files /dev/null and b/images/blank-widget-50x50.png differ diff --git a/images/dry.png b/images/dry.png new file mode 100644 index 0000000..3a81196 Binary files /dev/null and b/images/dry.png differ diff --git a/images/heat-widget-50x50.png b/images/heat-widget-50x50.png new file mode 100644 index 0000000..1e0f73a Binary files /dev/null and b/images/heat-widget-50x50.png differ diff --git a/images/logo-cutout.png b/images/logo-cutout.png new file mode 100644 index 0000000..153cd4c Binary files /dev/null and b/images/logo-cutout.png differ diff --git a/images/readme.md b/images/readme.md new file mode 100644 index 0000000..815deb0 --- /dev/null +++ b/images/readme.md @@ -0,0 +1,3 @@ +Files in this folder are the raw input graphics for the application. All of these ultimately +need to be run through tcMenuDesigner, or otherwise converted to hex to be stored in PROGMEM +- All graphics used by the application must consist of 1 color on a transparent background. \ No newline at end of file diff --git a/images/settings.png b/images/settings.png new file mode 100644 index 0000000..e49c58a Binary files /dev/null and b/images/settings.png differ diff --git a/images/spash.png b/images/spash.png new file mode 100644 index 0000000..a0cb455 Binary files /dev/null and b/images/spash.png differ diff --git a/images/spin.png b/images/spin.png new file mode 100644 index 0000000..9810b2d Binary files /dev/null and b/images/spin.png differ diff --git a/images/wash.png b/images/wash.png new file mode 100644 index 0000000..f98ef74 Binary files /dev/null and b/images/wash.png differ diff --git a/src/EncoderShim.cpp b/src/EncoderShim.cpp new file mode 100644 index 0000000..e055f7e --- /dev/null +++ b/src/EncoderShim.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Kevin Balthaser kevin@nybblebyte.com +*/ +// + +#include "EncoderShim.h" + +EncoderShim::EncoderShim() { + +} + +void EncoderShim::initForEncoder() { + + switches.init(internalDigitalIo(), SWITCHES_NO_POLLING, true); + + switches.addSwitchListener(BUTTON, this, NO_REPEAT, false); + setupRotaryEncoderWithInterrupt(ENC1, ENC2, this, HWACCEL_NONE, FULL_CYCLE); +} + +void EncoderShim::encoderHasChanged(int newValue) { + Serial.println("Encoder Turned"); + Serial.println(newValue); + + // Which way did the encoder go... + bool direction; + if (newValue < encoderValue) { + direction = false; + } else { + direction = true; + } + + encoderValue = newValue; + + menuMgr.valueChanged(newValue); + Serial.println("Check for encoderChangeFn"); + if (encoderChangeFn != nullptr) { + Serial.println("call encoderChangefn Callback"); + encoderChangeFn(direction, false); + } + +} + +void EncoderShim::onPressed(pinid_t pin, bool held) { + Serial.println("button down"); + if (encoderClickFn != nullptr) { + encoderClickFn(false, held); + } +} + +void EncoderShim::onReleased(pinid_t pin, bool held) { + Serial.println("button up"); + // always send false, since we don't want the normal tcMenu long press reset behavior, and we are acting + // on Pressed long press, not release. + menuMgr.onMenuSelect(false); +} + +void EncoderShim::registerChangeCallback(EncoderShimFn callback) { + + encoderChangeFn = callback; + +} + +void EncoderShim::registerClickCallback(EncoderShimFn callback) { + + encoderClickFn = callback; + +} + +/** + * Reinitialize the encoder with a and b pins reversed. + */ +void EncoderShim::invertEncoderDirection() { + setupRotaryEncoderWithInterrupt(ENC2, ENC1, this, HWACCEL_NONE, FULL_CYCLE); +} + diff --git a/src/EncoderShim.h b/src/EncoderShim.h new file mode 100644 index 0000000..badb054 --- /dev/null +++ b/src/EncoderShim.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 Kevin Balthaser kevin@nybblebyte.com + * + * Create a shim that will sit in between the tcMenu layer, and the firmware. This will allow us to monitor the rotary + * encoder on every cycle to pick up on movement events, and fire any callback events. +*/ +// + +#include "picoPlatform.h" // Need the platform pin definitions +#include + +#ifndef KIWIBOARDFIRMWARE_ENCODERSHIM_H +#define KIWIBOARDFIRMWARE_ENCODERSHIM_H + + +/** The definition of a callback from EncoderShim object */ +typedef void (*EncoderShimFn)(bool direction, bool longPress); + +class EncoderShim : EncoderListener, SwitchListener { + + public: + EncoderShim(); + + /** + * Initialize the encoder and button for pins defined in picoPlatform. This must be called + * before setupMenu. + */ + void initForEncoder(); + + /** + * Register the function that should be called when the encoder value changes. + */ + void registerChangeCallback(EncoderShimFn callback); + + /** + * Register the function that should be called when the encoder button has been pressed. + */ + void registerClickCallback(EncoderShimFn callback); + + /** + * Swap the two encoder pins, this will invert the direction of the encoder in all modes. + */ + void invertEncoderDirection(); + + private: + void encoderHasChanged(int newValue) override; + + void onPressed(pinid_t pin, bool held) override; + + void onReleased(pinid_t pin, bool held) override; + + // Callback Functions for click and change + EncoderShimFn encoderChangeFn = nullptr; + EncoderShimFn encoderClickFn = nullptr; + + int encoderValue = 0;// last known encoder value + +}; + + +#endif //KIWIBOARDFIRMWARE_ENCODERSHIM_H diff --git a/src/KiwiBoardFirmware_main.cpp b/src/KiwiBoardFirmware_main.cpp index 036d99d..2b736f2 100644 --- a/src/KiwiBoardFirmware_main.cpp +++ b/src/KiwiBoardFirmware_main.cpp @@ -8,6 +8,8 @@ #include #include "splashScreen.h" #include "KiwiBoardFirmware_main.h" +#include "EncoderShim.h" +#include "heat.h" #ifdef SCREENCAP #include "screenServer.h" @@ -17,11 +19,13 @@ #include "MenuChangeObserver.h" // Version Number -const char VERSION_NUM[] PROGMEM = "1.00"; +const char VERSION_NUM[] PROGMEM = "1.1.0"; + PicoPlatform *platform; -MotorControl *motorControl; +MotorControl *motorControl = nullptr; MenuChangeObserver *observer; +EncoderShim *encoderShim; // Error occurred, in HALT state. bool HALT = false; @@ -54,8 +58,6 @@ void setup() { // Init the graphics subsystem and trigger the splash. gfx.begin(); gfx.setRotation(3); - - // turn on the LED if DMA init successfully.. gfx.initDMA(true); showSplash(); @@ -63,6 +65,12 @@ void setup() { delay(2000); gfx.fillScreen(TFT_BLACK); + // Setup switches and encoder? + encoderShim = new EncoderShim(); + encoderShim->initForEncoder(); + encoderShim->registerChangeCallback(handleEncoderMove); + encoderShim->registerClickCallback(checkLongPress); + menuRunTime.setReadOnly(true); setupMenu(); menuMgr.load(0xfadf, nullptr); @@ -71,7 +79,7 @@ void setup() { motorControl = new MotorControl(); motorControl->initMotionController(platform, menuGlobalScaler.getIntValueIncludingOffset(), menuIRun.getIntValueIncludingOffset(), - menustealthTransition.getIntValueIncludingOffset()); + menuStealthChop.getBoolean()); motorControl->setStoppedCallback(stoppedCallback); @@ -81,19 +89,17 @@ void setup() { // Check for encoder inversion.. if (menuInvertEncoder.getBoolean()) { // Inversion selected, reinitialize the encoder plugin with the pins reversed. - Serial.println("Encoder inversion requested.. reinit menuMgr with encoder flipped"); - menuMgr.initForEncoder(&renderer, &menuRunTime, ENC2, ENC1, BUTTON); + encoderShim->invertEncoderDirection(); + // reinit menu system so encoder is configured properly. + setupMenu(); } observer = new MenuChangeObserver(&menuMgr, &menuRunTime, &menuWash); menuMgr.addChangeNotification(observer); - menuVersion.setTextValue(VERSION_NUM,true); + menuVersion.setTextValue(VERSION_NUM, true); setMenuOptions(); - - setTitlePressedCallback(titleBarClick); - scheduleTasks(); } @@ -150,8 +156,6 @@ void ui_tick() { // Check for motor status, if overheated or shorted, alert user TMC5160::DriverStatus curStatus = motorControl->getDriverStatus(); - Serial.println("Driver Status:"); - Serial.println(curStatus); if (curStatus != TMC5160::DriverStatus::OK) { // launch error dialog motorErrorDialog(curStatus); @@ -165,6 +169,18 @@ void ui_tick() { drawable->startDraw(); drawable->drawXBitmap(Coord(135, 0), Coord(50, 50), KiwiLogoWidIcon0); drawable->endDraw(); + + } + + // Check for heat icon, if the heater is on, show the icon, otherwise swap in the blank widget + if (platform->isHeaterEnabled()) { + if (HeatWidget.getCurrentState() != 1) { + HeatWidget.setCurrentState(1); + } + } else { + if (HeatWidget.getCurrentState() !=0) { + HeatWidget.setCurrentState(0); + } } } @@ -219,26 +235,6 @@ void motorErrorDialog(TMC5160::DriverStatus status) { } } -/** - * Show version dialog. - * - * @param id - */ -void titleBarClick(int id) { - - const char error[] PROGMEM = "Free Heap Usage"; - - BaseDialog *dlg = renderer.getDialog(); - if (dlg) { - dlg->setButtons(BTNTYPE_NONE, BTNTYPE_CLOSE); - dlg->show(error, false); - int freeHeap = rp2040.getFreeHeap(); - char cstr[16]; - itoa(freeHeap, cstr, 10); - dlg->copyIntoBuffer(cstr); - } -} - void CALLBACK_FUNCTION wash(int id) { run(0, &menuWash); } @@ -291,7 +287,7 @@ void CALLBACK_FUNCTION GlobalScalerChanged(int id) { // init motorControl->initMotionController(platform, menuGlobalScaler.getIntValueIncludingOffset(), menuIRun.getIntValueIncludingOffset(), - menustealthTransition.getIntValueIncludingOffset()); + menuStealthChop.getBoolean()); settingsChanged = true; } @@ -311,7 +307,7 @@ void CALLBACK_FUNCTION iRunChanged(int id) { // init motorControl->initMotionController(platform, menuGlobalScaler.getIntValueIncludingOffset(), menuIRun.getIntValueIncludingOffset(), - menustealthTransition.getIntValueIncludingOffset()); + menuStealthChop.getBoolean()); settingsChanged = true; } @@ -324,15 +320,22 @@ void CALLBACK_FUNCTION backlightChange(int id) { PicoPlatform::setBacklight(newVal); } + /** - * Callback for when the user changes the PWM Transition time. The motor controller needs to be stopped, and - * reconfigured. + * Callback for when the user changes the StealthChop setting. * * @param id */ -void CALLBACK_FUNCTION stealthTransitionChanged(int id) { +void CALLBACK_FUNCTION stealthChopChange(int id) { + + // Shut off the TMC + platform->enableMotor(false); + delay(100); // wait for everything to settle... + // init + motorControl->initMotionController(platform, menuGlobalScaler.getIntValueIncludingOffset(), + menuIRun.getIntValueIncludingOffset(), + menuStealthChop.getBoolean()); - motorControl->setPwmTransitionTime(menustealthTransition.getIntValueIncludingOffset()); settingsChanged = true; } @@ -432,10 +435,6 @@ void setMenuOptions() { GridPosition::JUSTIFY_CENTER_VALUE_ONLY, MenuBorder(0)); // Settings for the Settings menu - // // here is how we completely redefine the drawing of a specific item, you can also define for submenu or default - //color_t specialPalette[] { RGB(0, 255, 0), RGB(255, 0, 0), RGB(70, 70, 70), RGB(0, 0, 255) }; - - // TODO work out these how to style these, because currently, you can't see the cursor when editing multi part large numbers factory.setDrawingPropertiesAllInSub(ItemDisplayProperties::COMPTYPE_ITEM, menuSettings.getId(), settingsMenuPalette, MenuPadding(4), nullptr, 4, 2, 36, @@ -464,8 +463,10 @@ void setMenuOptions() { // Blank the title bar so that we can render the logo overtop of it. (Save a draw call to blank the rectangle) appTitleMenuItem.setTitleOverridePgm(""); + // Setup heat icon? + renderer.setFirstWidget(&HeatWidget); + // Black on white cursor -// factory.setSelectedColors(RGB(255, 0, 0), RGB(0, 255, 0)); factory.setSelectedColors(RGB(255, 255, 255), RGB(0, 0, 0)); // Home the user to the wash menu option @@ -500,32 +501,24 @@ void setIconStopped(MenuItem *icon) { tcgfx::ConfigurableItemDisplayPropertiesFactory::refreshCache(); } -void renderTimer(unsigned int encoderValue, RenderPressMode clicked) { - - // Test manually rendering the timer to see if it can be done smoother.. - // it didnt really help.. - //Serial.println("Render callback... we own display"); - - int xpos = 100; - // Render the timer box.. maybe ? - TimeStorage ts = menuRunTime.getTime(); - - if (ts.minutes == 0 && ts.seconds == 0) { - // renderer.giveBackDisplay(); - } else { - - gfx.drawRect(0, 50, 320, 100, TFT_WHITE); - gfx.fillRect(0, 52, 320, 100, TFT_WHITE); - gfx.setTextColor(TFT_BLACK); - - xpos += gfx.drawNumber(ts.minutes, xpos, 70, 7); - xpos += gfx.drawChar(':', xpos, 70, 7); - gfx.drawNumber(ts.seconds, xpos, 70, 7); +void checkLongPress(bool direction, bool held) { + + // Check for a long press... no idea what menu ... but whatever? + if (held) { + // what are we long pressing on? + if(menuMgr.findCurrentActive()->getId() == menuSpin.getId() || menuMgr.findCurrentActive()->getId() == menuWash.getId() ) { + if (platform->isHeaterEnabled()) { + // cancel preheat + platform->enableHeater(false); + } else { + platform->startPreheat(); + } + } } } /** - * Reset all of the icons back to their original state, reset the selecton color value. + * Reset all of the icons back to their original state, reset the selection color value. */ void resetIcons() { @@ -539,7 +532,6 @@ void resetIcons() { factory.addImageToCache(DrawableIcon(menuDry.getId(), iconSize, DrawableIcon::ICON_XBITMAP, CycleIconsBitmap2)); factory.addImageToCache( DrawableIcon(menuSettings.getId(), iconSize, DrawableIcon::ICON_XBITMAP, CycleIconsBitmap3)); - MenuPadding perSidePadding(3, 3, 3, 3); if (stoppedButton != nullptr) { MenuPadding buttonPadding(4, 4, 4, 4); @@ -570,3 +562,44 @@ void screenCaptureTask() { } #endif } + +/** + * Handle an encoder movement. Only do anything if we are actively running a program, otherwise we don't care + * + * @param direction + * @param held + */ +void handleEncoderMove(bool direction, bool held) { + +// Serial.println("handleEncoderMove..."); +// if (motorControl != nullptr && motorControl->isRunning()) { +// Serial.println("program running.. change speed"); +// // We are running, track left / right as speed changes.. +// +// int curSpeed = motorControl->getMotorSpeed(); +// +// // TODO Add bounds... +// if (!direction) { +// +// curSpeed = curSpeed - 10; +// } else { +// curSpeed = curSpeed + 10; +// } +// +// motorControl->overrideMotorSpeed(curSpeed); +// Serial.println(curSpeed); + + +// BaseDialog *dlg = renderer.getDialog(); +// if (dlg) { +// dlg->setButtons(BTNTYPE_CLOSE, BTNTYPE_NONE); +// dlg->show("Motor Speed Changed", false); +//// char cstr[16]; +//// itoa(curSpeed, cstr, 10); +//// +//// dlg->copyIntoBuffer(cstr); +// } + + //} + +} diff --git a/src/KiwiBoardFirmware_main.h b/src/KiwiBoardFirmware_main.h index 62927a6..8d5744f 100644 --- a/src/KiwiBoardFirmware_main.h +++ b/src/KiwiBoardFirmware_main.h @@ -5,6 +5,7 @@ */ #include +#include "EncoderShim.h" #ifndef KIWIBOARDFIRMWARE_KIWIBOARDFIRMWARE_MAIN_H #define KIWIBOARDFIRMWARE_KIWIBOARDFIRMWARE_MAIN_H @@ -51,7 +52,8 @@ void setIconStopped(MenuItem* icon); void resetIcons(); -void renderTimer(unsigned int encoderValue, RenderPressMode clicked); +void checkLongPress(bool direction, bool clicked); +void handleEncoderMove(bool direction, bool held); #endif //KIWIBOARDFIRMWARE_KIWIBOARDFIRMWARE_MAIN_H diff --git a/src/KiwiBoardFirmware_menu.cpp b/src/KiwiBoardFirmware_menu.cpp index 0ce777a..7128c45 100644 --- a/src/KiwiBoardFirmware_menu.cpp +++ b/src/KiwiBoardFirmware_menu.cpp @@ -1,4 +1,4 @@ -/* +/* The code in this file uses open source libraries provided by thecoderscorner DO NOT EDIT THIS FILE, IT WILL BE GENERATED EVERY TIME YOU USE THE UI DESIGNER @@ -22,10 +22,10 @@ GraphicsDeviceRenderer renderer(30, applicationInfo.name, &gfxDrawable); // Global Menu Item declarations RENDERING_CALLBACK_NAME_INVOKE(fnVersionRtCall, textItemRenderFn, "Version", -1, NO_CALLBACK) TextMenuItem menuVersion(fnVersionRtCall, "1.00", 43, 10, NULL); -const AnalogMenuInfo minfostealthTransition = { "PWM Threshold ", 42, 92, 2000, stealthTransitionChanged, 0, 1, "" }; -AnalogMenuItem menustealthTransition(&minfostealthTransition, 0, &menuVersion, INFO_LOCATION_PGM); +const BooleanMenuInfo minfoStealthChop = { "StealthChop", 45, 96, 1, stealthChopChange, NAMING_ON_OFF }; +BooleanMenuItem menuStealthChop(&minfoStealthChop, true, &menuVersion, INFO_LOCATION_PGM); const AnalogMenuInfo minfoIRun = { "IRun", 33, 77, 31, iRunChanged, 0, 1, "" }; -AnalogMenuItem menuIRun(&minfoIRun, 17, &menustealthTransition, INFO_LOCATION_PGM); +AnalogMenuItem menuIRun(&minfoIRun, 17, &menuStealthChop, INFO_LOCATION_PGM); const AnalogMenuInfo minfoGlobalScaler = { "Global Scaler", 32, 75, 255, GlobalScalerChanged, 0, 1, "" }; AnalogMenuItem menuGlobalScaler(&minfoGlobalScaler, 148, &menuIRun, INFO_LOCATION_PGM); const BooleanMenuInfo minfoInvertEncoder = { "Invert Encoder", 34, 79, 1, NO_CALLBACK, NAMING_YES_NO }; @@ -33,8 +33,10 @@ BooleanMenuItem menuInvertEncoder(&minfoInvertEncoder, false, &menuGlobalScaler, const SubMenuInfo minfoAdvanced = { "Advanced", 30, 0xffff, 0, NO_CALLBACK }; BackMenuItem menuBackAdvanced(&minfoAdvanced, &menuInvertEncoder, INFO_LOCATION_PGM); SubMenuItem menuAdvanced(&minfoAdvanced, &menuBackAdvanced, NULL, INFO_LOCATION_PGM); +const AnalogMenuInfo minfoMaxPreheat = { "Preheat", 44, 94, 9, settings_changed, 1, 1, "min" }; +AnalogMenuItem menuMaxPreheat(&minfoMaxPreheat, 4, NULL, INFO_LOCATION_PGM); const AnalogMenuInfo minfocooldownTime = { "Cool Time", 19, 21, 9, settings_changed, 1, 1, "min" }; -AnalogMenuItem menucooldownTime(&minfocooldownTime, 1, NULL, INFO_LOCATION_PGM); +AnalogMenuItem menucooldownTime(&minfocooldownTime, 1, &menuMaxPreheat, INFO_LOCATION_PGM); const BooleanMenuInfo minfofanCooldown = { "Cooldown", 18, 20, 1, settings_changed, NAMING_ON_OFF }; BooleanMenuItem menufanCooldown(&minfofanCooldown, true, &menucooldownTime, INFO_LOCATION_PGM); const AnalogMenuInfo minfodry_speed = { "Speed", 17, 18, 100, settings_changed, 50, 1, "rpm" }; @@ -48,8 +50,8 @@ const AnalogMenuInfo minfospinAMAX = { "Accel", 40, 88, 2000, settings_changed, AnalogMenuItem menuspinAMAX(&minfospinAMAX, 375, NULL, INFO_LOCATION_PGM); const AnalogMenuInfo minfospin_speed = { "Speed", 14, 14, 350, settings_changed, 50, 1, "rpm" }; AnalogMenuItem menuspin_speed(&minfospin_speed, 50, &menuspinAMAX, INFO_LOCATION_PGM); -const AnalogMenuInfo minfospin_duration = { "Time", 13, 12, 10, settings_changed, 1, 1, "min" }; -AnalogMenuItem menuspin_duration(&minfospin_duration, 0, &menuspin_speed, INFO_LOCATION_PGM); +const AnalogMenuInfo minfospin_duration = { "Time", 13, 12, 119, settings_changed, 1, 1, "sec" }; +AnalogMenuItem menuspin_duration(&minfospin_duration, 44, &menuspin_speed, INFO_LOCATION_PGM); const SubMenuInfo minfoSpinSettings = { "Spin", 12, 0xffff, 0, NO_CALLBACK }; BackMenuItem menuBackSpinSettings(&minfoSpinSettings, &menuspin_duration, INFO_LOCATION_PGM); SubMenuItem menuSpinSettings(&minfoSpinSettings, &menuBackSpinSettings, &menuDrySettings, INFO_LOCATION_PGM); @@ -94,8 +96,7 @@ void setupMenu() { gfx.begin(); gfx.setRotation(3); renderer.setUpdatesPerSecond(10); - switches.init(internalDigitalIo(), SWITCHES_NO_POLLING, true); - menuMgr.initForEncoder(&renderer, &menuRunTime, ENC1, ENC2, BUTTON); + menuMgr.initWithoutInput(&renderer, &menuRunTime); renderer.setTitleMode(BaseGraphicalRenderer::TITLE_ALWAYS); renderer.setUseSliderForAnalog(false); installDarkModeModernTheme(renderer, MenuFontDef(nullptr, 4), MenuFontDef(nullptr, 4), false); diff --git a/src/KiwiBoardFirmware_menu.h b/src/KiwiBoardFirmware_menu.h index a061790..27b480b 100644 --- a/src/KiwiBoardFirmware_menu.h +++ b/src/KiwiBoardFirmware_menu.h @@ -1,4 +1,4 @@ -/* +/* The code in this file uses open source libraries provided by thecoderscorner DO NOT EDIT THIS FILE, IT WILL BE GENERATED EVERY TIME YOU USE THE UI DESIGNER @@ -18,7 +18,7 @@ #include #include #include -#include "picoPlatform.h" + // variables we declare that you may need to access extern const PROGMEM ConnectorLocalInfo applicationInfo; extern TFT_eSPI gfx; @@ -30,12 +30,13 @@ extern GraphicsDeviceRenderer renderer; // Global Menu Item exports extern TextMenuItem menuVersion; -extern AnalogMenuItem menustealthTransition; +extern BooleanMenuItem menuStealthChop; extern AnalogMenuItem menuIRun; extern AnalogMenuItem menuGlobalScaler; extern BooleanMenuItem menuInvertEncoder; extern BackMenuItem menuBackAdvanced; extern SubMenuItem menuAdvanced; +extern AnalogMenuItem menuMaxPreheat; extern AnalogMenuItem menucooldownTime; extern BooleanMenuItem menufanCooldown; extern AnalogMenuItem menudry_speed; @@ -75,7 +76,7 @@ void CALLBACK_FUNCTION dry(int id); void CALLBACK_FUNCTION iRunChanged(int id); void CALLBACK_FUNCTION settings_changed(int id); void CALLBACK_FUNCTION spin(int id); -void CALLBACK_FUNCTION stealthTransitionChanged(int id); +void CALLBACK_FUNCTION stealthChopChange(int id); void CALLBACK_FUNCTION wash(int id); #endif // MENU_GENERATED_CODE_H diff --git a/src/heat.h b/src/heat.h new file mode 100644 index 0000000..5c52e1e --- /dev/null +++ b/src/heat.h @@ -0,0 +1,46 @@ +// Heat icon=0, width=50, height=50, size=350 +const uint8_t HeatWidIcon0[] PROGMEM = { +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, +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, +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, +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, +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, +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 +}; +// Heat icon=1, width=50, height=50, size=350 +const uint8_t HeatWidIcon1[] PROGMEM = { +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x38,0x0c,0x07,0x00,0x00,0x00,0x00,0x78,0x1c,0x07,0x00,0x00,0x00,0x00,0x70,0x38, +0x0e,0x00,0x00,0x00,0x00,0xe0,0x38,0x0c,0x00,0x00,0x00,0x00,0xe0,0x30,0x1c,0x00,0x00,0x00,0x00,0xe0, +0x30,0x1c,0x00,0x00,0x00,0x00,0xe0,0x30,0x1c,0x00,0x00,0x00,0x00,0xe0,0x38,0x0c,0x00,0x00,0x00,0x00, +0x60,0x38,0x0e,0x00,0x00,0x00,0x00,0x70,0x18,0x0e,0x00,0x00,0x00,0x00,0x70,0x1c,0x06,0x00,0x00,0x00, +0x00,0x38,0x0c,0x07,0x00,0x00,0x00,0x00,0x38,0x0e,0x03,0x00,0x00,0x00,0x00,0x1c,0x86,0x03,0x00,0x00, +0x00,0x00,0x1c,0x87,0x01,0x00,0x00,0x00,0x00,0x0c,0xc7,0x01,0x00,0x00,0x00,0x00,0x0e,0xc3,0x01,0x00, +0x00,0x00,0x00,0x8e,0xc3,0x00,0x00,0x00,0x00,0x00,0x8e,0xc3,0x00,0x00,0x00,0x00,0x00,0x0e,0xc3,0x01, +0x00,0x00,0x00,0x00,0x0c,0xc7,0x01,0x00,0x00,0x00,0x00,0x1c,0x87,0x03,0x00,0x00,0x00,0x00,0x10,0x04, +0x00,0x00,0x00,0x00,0xc0,0xff,0xff,0xff,0x00,0x00,0x00,0xe0,0xff,0xff,0xff,0x01,0x00,0x00,0xe0,0xff, +0xff,0xff,0x01,0x00,0x00,0xc0,0xff,0xff,0xff,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; +const uint8_t* const HeatWidIcons[] PROGMEM = { HeatWidIcon0, HeatWidIcon1 }; + +// Widget Generator heat +TitleWidget HeatWidget(HeatWidIcons, 2, 50, 50, nullptr); diff --git a/src/motorControl.cpp b/src/motorControl.cpp index dcbc691..ddcd61b 100644 --- a/src/motorControl.cpp +++ b/src/motorControl.cpp @@ -5,7 +5,7 @@ #include "motorControl.h" #include "picoPlatform.h" -void MotorControl::initMotionController(PicoPlatform *curPlatform, uint16_t globalScaler, uint16_t iRun, uint16_t transition) { +void MotorControl::initMotionController(PicoPlatform *curPlatform, uint16_t globalScaler, uint16_t iRun, bool stealthChop) { this->platform = curPlatform; // Set the current platform. // start motor controller. wants to be enabled to talk to it.. @@ -18,16 +18,22 @@ void MotorControl::initMotionController(PicoPlatform *curPlatform, uint16_t glob motorParams.irun = iRun; // 31 is max running current, adjust accordingly. motorParams.ihold = 0; // holding current, 0 enables freewheel - // Library initializes with StealthChop enabled. motor = new TMC5160_SPI(TMC_SS, TMC5160::DEFAULT_F_CLK, SPISettings(1000000, MSBFIRST, SPI_MODE0), SPI1); motor->begin(powerStageParams, motorParams, TMC5160::NORMAL_MOTOR_DIRECTION); // Disable the transition to SpreadCycle, we don't need accuracy, prefer StealthChop - // motor->writeRegister(TMC5160_Reg::TPWMTHRS, 0); - motor->writeRegister(TMC5160_Reg::TPWMTHRS, transition); - + if (stealthChop) { + // disable transition to SpreadCycle + motor->writeRegister(TMC5160_Reg::TPWMTHRS, 0); + } else { + // turn off stealth chop in the gconf register + TMC5160_Reg::GCONF_Register gconf = { 0 }; + gconf.en_pwm_mode = false; + gconf.shaft = TMC5160::NORMAL_MOTOR_DIRECTION; + motor->writeRegister(TMC5160_Reg::GCONF, gconf.value); + } motor->stop(); // Ensure the controller is initialized with the motor stopped @@ -93,6 +99,7 @@ void MotorControl::startProgram(int programId, SETTINGS currentSettings) { // Set speed in full steps per second. motor->setMaxSpeed(rpmToVmax(currentSettings.wash_speed)); + state.rpm = currentSettings.wash_speed; // Register for VMAX is expressed in uSteps / t // t is 1.398101 at 12mhz @@ -109,7 +116,8 @@ void MotorControl::startProgram(int programId, SETTINGS currentSettings) { state.direction = false; } else if (programId == 1) { // spin-off.. - state.run_end = state.run_start + (currentSettings.spin_duration * 60000); + // Spin is configured in seconds... not minutes... + state.run_end = state.run_start + (currentSettings.spin_duration * 1000); state.isRunning = true; // Enable drive, active low @@ -123,6 +131,7 @@ void MotorControl::startProgram(int programId, SETTINGS currentSettings) { motor->setAcceleration(currentSettings.spin_amax); motor->setMaxSpeed(rpmToVmax(currentSettings.spin_speed)); // Full steps per second + state.rpm = currentSettings.spin_speed; state.direction = true; } else { @@ -142,6 +151,7 @@ void MotorControl::startProgram(int programId, SETTINGS currentSettings) { motor->setRampMode(TMC5160::VELOCITY_MODE); motor->setAcceleration(600); motor->setMaxSpeed(rpmToVmax(currentSettings.dry_speed)); + state.rpm = currentSettings.dry_speed; state.direction = true; } @@ -275,12 +285,17 @@ float MotorControl::rpmToVmax(float rpm) { return vmax; } -void MotorControl::setPwmTransitionTime(int transitionTime) { - // Disable TMC, Enable, Wait to stabalize, and set the value - platform->enableMotor(false); - delay(10); - platform->enableMotor(true); - delay(10); - motor->writeRegister(TMC5160_Reg::TPWMTHRS, transitionTime); +int MotorControl::getMotorSpeed() { + return state.rpm; +} + +void MotorControl::overrideMotorSpeed(int speedRPM) { + + state.rpm = speedRPM; + + // reconfigure VMAX? This might cause problems in wash cycle.. + + motor->setMaxSpeed(rpmToVmax(speedRPM)); + } diff --git a/src/motorControl.h b/src/motorControl.h index 3624069..df31aa3 100644 --- a/src/motorControl.h +++ b/src/motorControl.h @@ -20,7 +20,7 @@ class MotorControl : public Executable /** * Initialize the TMC5160 motion controller running on the provided platform. */ - void initMotionController(PicoPlatform *curPlatform, uint16_t globalScaler, uint16_t iRun, uint16_t transition); + void initMotionController(PicoPlatform *curPlatform, uint16_t globalScaler, uint16_t iRun, bool stealthChop); void stopMotion(); void startProgram(int programId, SETTINGS currentSettings); @@ -50,14 +50,23 @@ class MotorControl : public Executable */ void setStoppedCallback(MotorCallbackFn stopFn); + /** - * Set the value of TPWMTHRS, the transition velocity between StealthChop and SpreadCycle. This - * is set in terms of TSTEP (time per micro-step) + * Set if StealthChop should be enabled, or if we should only use SpreadCycle. * - * @param transitionTime + * @param enabled + */ + void setStealthChop(bool enabled); + + /** + * Return the current configured motor speed. */ - void setPwmTransitionTime(int transitionTime); + int getMotorSpeed(); + /** + * Override the current motor speed. + */ + void overrideMotorSpeed(int speedRPM); /** * Called by task loop. This is what will process any motor control tasks necessary to execute programs. diff --git a/src/picoPlatform.cpp b/src/picoPlatform.cpp index 0b4d8b1..0590677 100644 --- a/src/picoPlatform.cpp +++ b/src/picoPlatform.cpp @@ -31,7 +31,7 @@ void PicoPlatform::initializePlatform() { pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); - // Remap IO to the correct pins for hardware SPI 0 + // Remap IO to the correct pins for hardware SPI 1 SPI1.setRX(TMC_MISO); SPI1.setTX(TMC_MOSI); SPI1.setSCK(TMC_SCLK); @@ -42,8 +42,8 @@ void PicoPlatform::initializePlatform() { pinMode(LCD_BACKLIGHT, OUTPUT); analogWrite(LCD_BACKLIGHT, 125); - - //Setup SPI0 for the TFT + + //Setup Hardware SPI0 for the TFT SPI.setCS(LCD_CS); SPI.setRX(LCD_MISO); SPI.setTX(LCD_MOSI); @@ -70,6 +70,9 @@ void PicoPlatform::enableHeater(bool activate) { // If we are turning the heater off, the cooldown logic will turn off the fan if (activate) { enableFan(true); + if (in_preheat) { + in_preheat = false; // IF a preheat already happened, stop that process + } } } @@ -126,9 +129,9 @@ void PicoPlatform::exec() { if (in_cooldown) { Serial.println("in cooldown"); - Serial.print("- cooldown_end = " ) ; + Serial.print("- cooldown_end = "); Serial.println(cooldown_end); - Serial.print("- millis.. " ); + Serial.print("- millis.. "); Serial.println(millis()); if (millis() >= cooldown_end) { Serial.println("cooldown over, turn off fan"); @@ -137,6 +140,13 @@ void PicoPlatform::exec() { } } + // Check for preheat ending.. + if (in_preheat) { + if (millis() >= preheat_end) { + startCooldown(); + } + } + // Heartbeat led = !led; digitalWrite(LED_BUILTIN, led); @@ -148,6 +158,7 @@ void PicoPlatform::startCooldown() { // turn off the heat enableHeater(false); + in_preheat = false; if (settings.fanCooldown) { // cooldown enabled, calculate the fan end time, mark start time. ::tick will handle turning fan off @@ -168,3 +179,15 @@ void PicoPlatform::setBacklight(int value) { analogWrite(LCD_BACKLIGHT, backlight); } + +void PicoPlatform::startPreheat() { + SETTINGS settings = getSettings(); + + // turn on the heat + enableHeater(true); + + // preheat enabled, calculate the preheat end time, mark start time. ::tick will handle finishing preheat + preheat_start = millis(); + preheat_end = preheat_start + (settings.preheatTime * 60000); + in_preheat = true; +} diff --git a/src/picoPlatform.h b/src/picoPlatform.h index afff51f..653c3f3 100644 --- a/src/picoPlatform.h +++ b/src/picoPlatform.h @@ -73,6 +73,14 @@ class PicoPlatform : public Executable { */ void startCooldown(); + /** + * Start an optional preheat cycle. + * + * Look at the current configuration, calculate preheat end, and start time. A call to startCooldown + * will override any running preheat. + */ + void startPreheat(); + /** * To be called by task loop. This is charged with updating any state changes necessary with the * platform outside of direct motor control. @@ -94,6 +102,7 @@ class PicoPlatform : public Executable { bool isFanEnabled(); private: + bool in_preheat = false; bool in_cooldown = false; bool motor_enabled = false; bool heater_enabled = false; @@ -102,5 +111,7 @@ class PicoPlatform : public Executable { unsigned long cooldown_start; // when did a cooldown start. unsigned long cooldown_end; // when should a cooldown stop. + unsigned long preheat_start; + unsigned long preheat_end; }; \ No newline at end of file diff --git a/src/settings.cpp b/src/settings.cpp index 81ab3e5..3c7c929 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -22,6 +22,8 @@ SETTINGS getSettings() { rtn.cooldownTime = menucooldownTime.getIntValueIncludingOffset(); rtn.fanCooldown = menufanCooldown.getBoolean(); + rtn.preheatTime = menuMaxPreheat.getIntValueIncludingOffset(); + return rtn; } diff --git a/src/settings.h b/src/settings.h index 968a21e..39fb343 100644 --- a/src/settings.h +++ b/src/settings.h @@ -23,7 +23,9 @@ struct SETTINGS int dry_duration; int dry_speed; int cooldownTime; + int preheatTime; bool fanCooldown; + };