Recent changes

This commit is contained in:
Zoran Horvat
2019-05-21 15:31:59 +02:00
parent ecd84613ee
commit e2ce57ad81
26 changed files with 3981 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
.pio
.pioenvs
.piolibdeps
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json

67
.travis.yml Normal file
View File

@@ -0,0 +1,67 @@
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
# < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
# < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#
#
# Template #1: General project. Test it using existing `platformio.ini`.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio run
#
# Template #2: The project is intended to be used as a library with examples.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# env:
# - PLATFORMIO_CI_SRC=path/to/test/file.c
# - PLATFORMIO_CI_SRC=examples/file.ino
# - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N

7
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

Binary file not shown.

16
SMuFF.code-workspace Normal file
View File

@@ -0,0 +1,16 @@
{
"folders": [
{
"path": "."
},
{
"path": "D:\\Data\\PlatformIO\\SMuFF-Ifc\\SMuFF-Ifc"
}
],
"settings": {
"terminal.integrated.env.windows": {
"PATH": "C:\\Users\\Zoran\\.platformio\\penv\\Scripts;C:\\Users\\Zoran\\.platformio\\penv;C:\\Program Files (x86)\\Common Files\\Oracle\\Java\\javapath;C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program Files (x86)\\Intel\\iCLS Client\\;C:\\Program Files\\Intel\\iCLS Client\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;C:\\Program Files (x86)\\Windows Kits\\8.1\\Windows Performance Toolkit\\;C:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn\\;C:\\Program Files (x86)\\Microsoft SDKs\\TypeScript\\1.0\\;C:\\Program Files\\Microsoft SQL Server\\120\\Tools\\Binn\\;C:\\Program Files\\Microsoft\\Web Platform Installer\\;C:\\CODEGEAR\\INTERBASE\\BIN;C:\\Program Files (x86)\\QuickTime\\QTSystem\\;C:\\Program Files (x86)\\Common Files\\Acronis\\SnapAPI\\;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\Users\\Zoran\\AppData\\Local\\Android\\sdk\\platform-tools;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files\\Java\\jdk1.8.0_45\\bin;C:\\Program Files (x86)\\Android\\android-sdk\\tools;C:\\Program Files (x86)\\Android\\android-sdk\\platform-tools;C:\\apache-ant-1.9.3\\bin;C:\\Program Files (x86)\\nodejs\\;C:\\Program Files (x86)\\Git\\cmd;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Langmeier Software\\Langmeier Backup\\;C:\\Program Files (x86)\\Langmeier Software\\Langmeier Backup\\tools\\;C:\\Program Files\\Microsoft SQL Server\\130\\Tools\\Binn\\;C:\\Program Files\\Microsoft Network Monitor 3\\;C:\\WINDOWS\\System32\\OpenSSH\\;C:\\Program Files\\Git\\cmd;C:\\Program Files\\dotnet\\;C:\\Program Files (x86)\\SSH Communications Security\\SSH Secure Shell;C:\\Users\\Zoran\\AppData\\Roaming\\npm;C:\\Program Files (x86)\\Pstools;;C:\\Users\\Zoran\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Program Files (x86)\\Nmap;C:\\Users\\Zoran\\AppData\\Local\\Programs\\Microsoft VS Code\\bin;C:\\Program Files (x86)\\Common Files\\Oracle\\Java\\javapath;C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program Files (x86)\\Intel\\iCLS Client\\;C:\\Program Files\\Intel\\iCLS Client\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;C:\\Program Files (x86)\\Windows Kits\\8.1\\Windows Performance Toolkit\\;C:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn\\;C:\\Program Files (x86)\\Microsoft SDKs\\TypeScript\\1.0\\;C:\\Program Files\\Microsoft SQL Server\\120\\Tools\\Binn\\;C:\\Program Files\\Microsoft\\Web Platform Installer\\;C:\\CODEGEAR\\INTERBASE\\BIN;C:\\Program Files (x86)\\QuickTime\\QTSystem\\;C:\\Program Files (x86)\\Common Files\\Acronis\\SnapAPI\\;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\Users\\Zoran\\AppData\\Local\\Android\\sdk\\platform-tools;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files\\Java\\jdk1.8.0_45\\bin;C:\\Program Files (x86)\\Android\\android-sdk\\tools;C:\\Program Files (x86)\\Android\\android-sdk\\platform-tools;C:\\apache-ant-1.9.3\\bin;C:\\Program Files (x86)\\nodejs\\;C:\\Program Files (x86)\\Git\\cmd;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Langmeier Software\\Langmeier Backup\\;C:\\Program Files (x86)\\Langmeier Software\\Langmeier Backup\\tools\\;C:\\Program Files\\Microsoft SQL Server\\130\\Tools\\Binn\\;C:\\Program Files\\Microsoft Network Monitor 3\\;C:\\WINDOWS\\System32\\OpenSSH\\;C:\\Program Files\\Git\\cmd;C:\\Program Files\\dotnet\\;C:\\Program Files (x86)\\SSH Communications Security\\SSH Secure Shell;C:\\Users\\Zoran\\AppData\\Roaming\\npm;C:\\Program Files (x86)\\Pstools;;C:\\Users\\Zoran\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Program Files (x86)\\Nmap;C:\\Users\\Zoran\\AppData\\Local\\Programs\\Microsoft VS Code\\bin",
"PLATFORMIO_CALLER": "vscode"
}
}
}

39
include/README Normal file
View File

@@ -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

46
lib/README Normal file
View File

@@ -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 a 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 <Foo.h>
#include <Bar.h>
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

31
platformio.ini Normal file
View File

@@ -0,0 +1,31 @@
; 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
[platformio]
home_dir = D:/data/PlatformIO/
[env:megaatmega2560]
build_unflags = -Wunused-variable
platform = atmelavr
board = megaatmega2560
framework = arduino
upload_port = COM35
monitor_speed = 250000
lib_deps =
SPI
Wire
SD
# JSON library for the configuration file
ArduinoJson@6.10.1
# LC-Display library
U8g2@2.26.1
# Rotary-Encoder library
Encoder@1.4.1
#Free memory library
https://github.com/McNeight/MemoryFree

156
src/Config.cpp Normal file
View File

@@ -0,0 +1,156 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Module for reading configuration file (JSON-Format) from SD-Card
*/
#include "SMuFF.h"
#include <ArduinoJson.h>
Sd2Card card;
SdVolume volume;
SdFile root;
const size_t capacity = 1200;
void readConfig()
{
DynamicJsonDocument jsonDoc(capacity);
pinMode(SD_SS_PIN, OUTPUT);
digitalWrite(SD_SS_PIN, HIGH);
/*
if (card.init(SPI_FULL_SPEED, SD_SS_PIN)) {
uint32_t cardSize = card.cardSize();
__debug("Card type: ");
switch (card.type()) {
case SD_CARD_TYPE_SD1: __debug("SD"); break;
case SD_CARD_TYPE_SD2: __debug("SD2"); break;
case SD_CARD_TYPE_SDHC: __debug("SD%SC", cardSize < 70000000 ? "H" : "X"); break;
default: __debug("Unknown");
}
__debug("Card size: %u MB", cardSize);
if (volume.init(card)) {
__debug("Volume is FAT%d", volume.fatType());
root.openRoot(volume);
if(!root.isOpen()) {
__debug("Can't open root.");
}
else
root.ls(LS_R | LS_DATE | LS_SIZE);
}
}
*/
if (!SD.begin(SD_SS_PIN)) {
drawSDStatus(SD_ERR_INIT);
delay(5000);
return;
}
__debug("Trying to open config file '%s'", CONFIG_FILE);
File cfg = SD.open(CONFIG_FILE);
if (cfg) {
size_t fsize = cfg.size();
__debug("File size: %u", fsize);
if(fsize > capacity) {
showDialog(P_TitleConfigError, P_ConfigFail1, P_ConfigFail3, P_OkButtonOnly);
cfg.close();
return;
}
auto error = deserializeJson(jsonDoc, cfg);
if (error) {
__debug("deserializeJson() failed with code %s", error.c_str());
showDialog(P_TitleConfigError, P_ConfigFail1, P_ConfigFail2, P_OkButtonOnly);
}
else {
const char* selector = "Selector";
const char* revolver = "Revolver";
const char* feeder = "Feeder";
const char* maxSpeed = "MaxSpeed";
const char* invertDir = "InvertDir";
drawSDStatus(SD_READING_CONFIG);
int toolCnt = jsonDoc["ToolCount"];
smuffConfig.toolCount = (toolCnt > MIN_TOOLS && toolCnt < MAX_TOOLS) ? toolCnt : 5;
smuffConfig.firstToolOffset = jsonDoc[selector]["Offset"];
smuffConfig.toolSpacing = jsonDoc[selector]["Spacing"];
smuffConfig.stepsPerMM_X = jsonDoc[selector]["StepsPerMillimeter"];
smuffConfig.maxSteps_X = ((smuffConfig.toolCount-1)*smuffConfig.toolSpacing+smuffConfig.firstToolOffset) * smuffConfig.stepsPerMM_X;
smuffConfig.maxSpeed_X = jsonDoc[selector][maxSpeed];
smuffConfig.acceleration_X = jsonDoc[selector]["Acceleration"];
smuffConfig.invertDir_X = jsonDoc[selector][invertDir];
smuffConfig.endstopTrigger_X = jsonDoc[selector]["EndstopTrigger"];
smuffConfig.stepsPerRevolution_Y= jsonDoc[revolver]["StepsPerRevolution"];
smuffConfig.firstRevolverOffset = jsonDoc[revolver]["Offset"];
smuffConfig.revolverSpacing = smuffConfig.stepsPerRevolution_Y / 10;
smuffConfig.maxSpeed_Y = jsonDoc[revolver][maxSpeed];
smuffConfig.acceleration_Y = jsonDoc[revolver]["Acceleration"];
smuffConfig.resetBeforeFeed_Y = jsonDoc[revolver]["ResetBeforeFeed"];
smuffConfig.homeAfterFeed = jsonDoc[revolver]["HomeAfterFeed"];
smuffConfig.invertDir_Y = jsonDoc[revolver][invertDir];
smuffConfig.endstopTrigger_Y = jsonDoc[revolver]["EndstopTrigger"];
smuffConfig.externalControl_Z = jsonDoc[feeder]["ExternalControl"];
smuffConfig.stepsPerMM_Z = jsonDoc[feeder]["StepsPerMillimeter"];
smuffConfig.acceleration_Z = jsonDoc[feeder]["Acceleration"];
smuffConfig.maxSpeed_Z = jsonDoc[feeder][maxSpeed];
smuffConfig.insertSpeed_Z = jsonDoc[feeder]["InsertSpeed"];
smuffConfig.invertDir_Z = jsonDoc[feeder][invertDir];
smuffConfig.endstopTrigger_Z = jsonDoc[feeder]["EndstopTrigger"];
smuffConfig.reinforceLength = jsonDoc[feeder]["ReinforceLength"];
smuffConfig.unloadRetract = jsonDoc[feeder]["UnloadRetract"];
smuffConfig.unloadPushback = jsonDoc[feeder]["UnloadPushback"];
smuffConfig.pushbackDelay = jsonDoc[feeder]["PushbackDelay"];
int contrast = jsonDoc["LCDContrast"];
smuffConfig.lcdContrast = (contrast > MIN_CONTRAST && contrast < MAX_CONTRAST) ? contrast : DSP_CONTRAST;
smuffConfig.bowdenLength = jsonDoc["BowdenLength"];
int i2cAdr = jsonDoc["I2CAddress"];
smuffConfig.i2cAddress = (i2cAdr > 0 && i2cAdr < 255) ? i2cAdr : I2C_SLAVE_ADDRESS;
smuffConfig.menuAutoClose = jsonDoc["MenuAutoClose"];
smuffConfig.delayBetweenPulses = jsonDoc["DelayBetweenPulses"];
smuffConfig.serial1Baudrate = jsonDoc["Serial1Baudrate"];
smuffConfig.serial2Baudrate = jsonDoc["Serial2Baudrate"];
smuffConfig.serialDueBaudrate = jsonDoc["SerialDueBaudrate"];
smuffConfig.fanSpeed = jsonDoc["FanSpeed"];
smuffConfig.powerSaveTimeout = jsonDoc["PowerSaveTimeout"];
smuffConfig.duetDirect = jsonDoc["Duet3DDirect"];
char* p = jsonDoc["UnloadCommand"];
if(p != NULL && strlen(p) > 0)
strlcpy(smuffConfig.unloadCommand, p, sizeof(smuffConfig.unloadCommand));
for(int i=0; i < smuffConfig.toolCount; i++) {
char tmp[10];
sprintf(tmp,"Tool%d", i);
memset(smuffConfig.materials[i], 0, sizeof(smuffConfig.materials[i]));
strlcpy(smuffConfig.materials[i], jsonDoc["Materials"][tmp], sizeof(smuffConfig.materials[i]));
}
//__debug("DONE reading config");
}
cfg.close();
}
else {
__debug("Open config file failed: handle = %s", !cfg ? "FALSE" : "TRUE");
drawSDStatus(SD_ERR_NOCONFIG);
delay(5000);
}
}

122
src/Config.h Normal file
View File

@@ -0,0 +1,122 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _SMUFF_CONFIG_H
#define _SMUFF_CONFIG_H
#define VERSION_STRING "V1.1"
#define VERSION_DATE "2019-05-12"
#define CONFIG_FILE "SMUFF.CFG"
#define SELECTOR 0
#define REVOLVER 1
#define FEEDER 2
#define NUM_STEPPERS 3
#define MIN_TOOLS 2
#define MAX_TOOLS 9
#define STEP_HIGH_X PORTA |= 0b00000001;
#define STEP_LOW_X PORTA &= ~0b00000001;
#define X_STEP_PIN 22
#define X_DIR_PIN 23
#define X_ENABLE_PIN 57
#define X_END_PIN 19
#define X_STEPS_PER_MM 800
#define STEP_HIGH_Y PORTA |= 0b00001000;
#define STEP_LOW_Y PORTA &= ~0b00001000;
#define Y_STEP_PIN 25
#define Y_DIR_PIN 26
#define Y_ENABLE_PIN 24
#define Y_END_PIN 18
#define STEP_HIGH_Z PORTA |= 0b10000000;
#define STEP_LOW_Z PORTA &= ~0b10000000;
#define Z_STEP_PIN 29
#define Z_DIR_PIN 39
#define Z_ENABLE_PIN 28
#define Z_END_PIN 38
#define Z_STEPS_PER_MM 136
#define BEEPER_PIN 37
#define BEEPER_FREQUENCY 1760
#define BEEPER_DURATION 90
#define BEEPER_UFREQUENCY 440
#define BEEPER_UDURATION 90
#define SERVO1_PIN 44
#define SERVO2_PIN 14
#define SD_SS_PIN 53
#define FAN_PIN 12
#define HEATER0_PIN 4
#define ENCODER1_PIN 2
#define ENCODER2_PIN 3
#define ENCODER_BUTTON_PIN 5
#define ENCODER_DELAY 4
#define ENCODER_DELAY_MENU 2
#define ENCODER_DELAY_OFS 1
#define DSP_CLOCK_PIN 52
#define DSP_DATA_PIN 51
#define DSP_CS_PIN 41
#define DSP_DC_PIN 40
#define DSP_RESET_PIN 27
#define DSP_BACKLIGHT_PIN 65
#define DSP_CONTRAST 200
#define MIN_CONTRAST 60
#define MAX_CONTRAST 250
#define TX2_PIN 16
#define RX2_PIN 17
#define I2C_SLAVE_ADDRESS 0x88
#define TX3_PIN 67
#define RX3_PIN 44
#define FIRST_TOOL_OFFSET 1.2 // values in millimeter
#define TOOL_SPACING 21.0 // values im millimeter
#define FIRST_REVOLVER_OFFSET 320 // values in steps
#define REVOLVER_SPACING 320 // values im steps
#define USER_MESSAGE_RESET 15 // value in seconds
#define MAX_LINES 5
#define MAX_LINE_LENGTH 80
#define POWER_SAVE_TIMEOUT 15 // value in seconds
#define BASE_FONT u8g2_font_6x12_t_symbols
#define BASE_FONT_BIG u8g2_font_7x14B_tf
#define SMALL_FONT u8g2_font_6x10_mr
#define STATUS_FONT u8g2_font_7x14_tf
#define LOGO_FONT u8g2_font_helvR08_tf
#define ICONIC_FONT u8g2_font_open_iconic_check_2x_t
#define EEPROM_SELECTOR_POS SELECTOR*sizeof(long)
#define EEPROM_REVOLVER_POS REVOLVER*sizeof(long)
#define EEPROM_FEEDER_POS FEEDER*sizeof(long)
#define EEPROM_TOOL 20
#define EEPROM_CONTRAST 22
#define EEPROM_TOOL_COUNT 24
#define EEPROM_FEED_LENGTH 26
#define EEPROM_1ST_TOOL_OFS 30
#define EEPROM_TOOL_SPACING 34
#define EEPROM_1ST_REV_OFS 38
#define EEPROM_REV_SPACING 42
#endif

583
src/GCodes.cpp Normal file
View File

@@ -0,0 +1,583 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Module for handling all the G-Codes supported
*/
#include "SMuFF.h"
#include "ZTimerLib.h"
#include "ZStepperLib.h"
#include "ZServo.h"
#include "GCodes.h"
extern ZStepper steppers[NUM_STEPPERS];
extern ZServo servo;
char* S_Param = (char*)"S";
char* P_Param = (char*)"P";
char* X_Param = (char*)"X";
char* Y_Param = (char*)"Y";
char* Z_Param = (char*)"Z";
char* E_Param = (char*)"E";
char* F_Param = (char*)"F";
char* C_Param = (char*)"C";
char* T_Param = (char*)"T";
char* N_Param = (char*)"N";
char* I_Param = (char*)"I";
char* J_Param = (char*)"J";
char* K_Param = (char*)"K";
char* R_Param = (char*)"R";
GCodeFunctions gCodeFuncsM[] = {
{ 80, dummy },
{ 81, dummy },
{ 104, dummy },
{ 105, dummy },
{ 108, dummy },
{ 109, dummy },
{ 220, dummy },
{ 221, dummy },
{ 18, M18 },
{ 20, M20 },
{ 42, M42 },
{ 84, M18 },
{ 106, M106 },
{ 107, M107 },
{ 110, M110 },
{ 111, M111 },
{ 114, M114 },
{ 115, M115 },
{ 117, M117 },
{ 119, M119 },
{ 201, M201 },
{ 203, M203 },
{ 206, M206 },
{ 250, M250 },
{ 280, M280 },
{ 300, M300 },
{ 500, M500 },
{ 503, M503 },
{ 700, M700 },
{ 701, M701 },
{ 999, M999 },
{ 2000, M2000 },
{ 2001, M2001 },
{ -1, NULL }
};
GCodeFunctions gCodeFuncsG[] = {
{ 0, G0 },
{ 1, G1 },
{ 4, G4 },
{ 12, G12 },
{ 28, G28 },
{ 90, G90 },
{ 91, G91 },
{ -1, NULL},
};
int param;
extern char tmp[128];
/*========================================================
* Class G
========================================================*/
bool dummy(const char* msg, String buf, int serial) {
int code = buf.toInt();
__debug("Ignored M-Code: M%d", code);
return true;
}
bool M18(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if(buf.length()==0) {
steppers[SELECTOR].setEnabled(false);
steppers[REVOLVER].setEnabled(false);
steppers[FEEDER].setEnabled(false);
}
else {
if(buf.indexOf(X_Param) != -1) {
steppers[SELECTOR].setEnabled(false);
}
else if(buf.indexOf(Y_Param) != -1) {
steppers[REVOLVER].setEnabled(false);
}
else if(buf.indexOf(Z_Param) != -1) {
steppers[FEEDER].setEnabled(false);
}
else {
stat = false;
}
}
return stat;
}
bool M20(const char* msg, String buf, int serial) {
if(!getParamString(buf, S_Param, tmp, sizeof(tmp))){
sprintf(tmp,"/");
}
if (SD.begin(SD_SS_PIN)) {
File root = SD.open(tmp);
listDir(root, 1, serial);
root.close();
return true;
}
sprintf_P(tmp, P_SD_InitError);
printResponse(tmp, serial);
return false;
}
bool M42(const char* msg, String buf, int serial) {
bool stat = true;
int pin;
printResponse(msg, serial);
if((pin = getParam(buf, P_Param)) == -1) {
pinMode(pin, OUTPUT);
if((param = getParam(buf, S_Param)) == -1) {
if(param >= 0 && param <= 255)
analogWrite(pin, param);
}
}
return stat;
}
bool M106(const char* msg, String buf, int serial) {
printResponse(msg, serial);
if((param = getParam(buf, S_Param)) == -1) {
param = 255;
}
analogWrite(FAN_PIN, param);
return true;
}
bool M107(const char* msg, String buf, int serial) {
printResponse(msg, serial);
analogWrite(FAN_PIN, 0);
return true;
}
bool M110(const char* msg, String buf, int serial) {
printResponse(msg, serial);
if((param = getParam(buf, N_Param)) != -1) {
currentLine = param;
}
return true;
}
bool M111(const char* msg, String buf, int serial) {
if((param = getParam(buf, S_Param)) != -1) {
testMode = param == 1;
}
return true;
}
bool M114(const char* msg, String buf, int serial) {
printResponse(msg, serial);
sprintf_P(tmp, P_AccelSpeed,
String(steppers[SELECTOR].getStepPositionMM()).c_str(),
String(steppers[REVOLVER].getStepPosition()).c_str(),
String(steppers[FEEDER].getStepPositionMM()).c_str());
printResponse(tmp, serial);
return true;
}
bool M115(const char* msg, String buf, int serial) {
sprintf_P(tmp, P_GVersion, VERSION_STRING, VERSION_DATE);
printResponse(tmp, serial);
return true;
}
bool M117(const char* msg, String buf, int serial) {
String umsg = buf;
umsg.replace("_", " ");
beep(1);
drawUserMessage(umsg);
return true;
}
bool M119(const char* msg, String buf, int serial) {
printResponse(msg, serial);
if((param = getParam(buf, Z_Param)) != -1) {
steppers[FEEDER].setEndstopHit(param);
}
printEndstopState(serial);
return true;
}
bool M201(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if(buf.length()==0) {
printAcceleration(serial);
return stat;
}
if((param = getParam(buf, X_Param)) != -1) {
if(param >= 200 && param <= 15000)
steppers[SELECTOR].setAcceleration(param);
else stat = false;
}
if((param = getParam(buf, Y_Param)) != -1) {
if(param >= 200 && param <= 15000)
steppers[REVOLVER].setAcceleration(param);
else stat = false;
}
if((param = getParam(buf, Z_Param)) != -1) {
if(param >= 200 && param <= 15000)
steppers[FEEDER].setAcceleration(param);
else stat = false;
}
return stat;
}
bool M203(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if(buf.length()==0) {
printSpeeds(serial);
return stat;
}
if((param = getParam(buf, X_Param)) != -1) {
if(param > 0 && param <= 10000)
steppers[SELECTOR].setMaxSpeed(param);
else stat = false;
}
if((param = getParam(buf, Y_Param)) != -1) {
if(param > 0 && param <= 10000) {
steppers[REVOLVER].setMaxSpeed(param);
//__debug("Revolver max speed: %d", steppers[REVOLVER].getMaxSpeed());
}
else stat = false;
}
if((param = getParam(buf, Z_Param)) != -1) {
if(param > 0 && param <= 10000)
steppers[FEEDER].setMaxSpeed(param);
else stat = false;
}
return stat;
}
bool M206(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if(buf.length()==0) {
printOffsets(serial);
return stat;
}
if((param = getParam(buf, X_Param)) != -1) {
if(param > 0 && param <= 10000)
smuffConfig.firstToolOffset = (float)param/10;
else stat = false;
}
if((param = getParam(buf, Y_Param)) != -1) {
if(param > 0 && param <= 8640) {
smuffConfig.firstRevolverOffset = param;
}
else stat = false;
}
return stat;
}
bool M250(const char* msg, String buf, int serial) {
bool stat = true;
if((param = getParam(buf, C_Param)) != -1) {
if(param >= 60 && param < 256) {
display.setContrast(param);
EEPROM.put(EEPROM_CONTRAST, param);
printResponse(msg, serial);
}
else
stat = false;
}
else {
printResponse(msg, serial);
char tmp[50];
sprintf_P(tmp, P_M250Response, smuffConfig.lcdContrast);
printResponse(tmp, serial);
}
return stat;
}
bool M280(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if((param = getParam(buf, S_Param)) != -1) {
if(!setServoPos(param))
stat = false;
}
else stat = false;
return stat;
}
bool M300(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if((param = getParam(buf, S_Param)) != -1) {
int frequency = param;
if((param = getParam(buf, P_Param)) != -1) {
tone(BEEPER_PIN, frequency, param);
}
else
stat = false;
}
else
stat = false;
return stat;
}
bool M500(const char* msg, String buf, int serial) {
printResponse(msg, serial);
saveSettings(serial);
return true;
}
bool M503(const char* msg, String buf, int serial) {
printResponse(msg, serial);
reportSettings(serial);
return true;
}
bool M700(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if(toolSelected > 0 && toolSelected <= MAX_TOOLS) {
getParamString(buf, S_Param, smuffConfig.materials[toolSelected], sizeof(smuffConfig.materials[0]));
//__debug("Material: %s\n",smuffConfig.materials[toolSelected]);
return loadFilament();
}
else
stat = false;
return stat;
}
bool M701(const char* msg, String buf, int serial) {
printResponse(msg, serial);
return unloadFilament();
}
bool M999(const char* msg, String buf, int serial) {
printResponse(msg, serial);
delay(500);
__asm__ volatile ("jmp 0x0000");
return true;
}
bool M2000(const char* msg, String buf, int serial) {
char s[80];
printResponse(msg, serial);
getParamString(buf, S_Param, tmp, sizeof(tmp));
if(strlen(tmp)>0) {
printResponse("B", serial);
for(unsigned i=0; i< strlen(tmp); i++) {
sprintf(s,"%d:", (char)tmp[i]);
printResponse(s, serial);
}
printResponse("10\n", serial);
}
return true;
}
bool M2001(const char* msg, String buf, int serial) {
printResponse(msg, serial);
getParamString(buf, S_Param, tmp, sizeof(tmp));
String data = String(tmp);
data.trim();
if(data.length() > 0) {
int ndx = 0;
int pos = 0;
if(data.startsWith("B")) {
printResponse(">>", serial);
ndx++;
do {
pos = data.indexOf(":", ndx);
int c;
if(pos != -1) {
c = data.substring(ndx, pos).toInt();
}
else {
c = data.substring(ndx).toInt();
}
if(c == 10) {
printResponse("\\n", serial);
}
else {
sprintf(tmp, "%c", c);
printResponse(tmp, serial);
}
ndx = pos + 1;
} while(pos != -1);
printResponse("<<\n", serial);
}
else {
printResponseP(P_WrongFormat, serial);
return false;
}
}
else
return false;
return true;
}
/*========================================================
* Class G
========================================================*/
bool G0(const char* msg, String buf, int serial) {
printResponse(msg, serial);
if((param = getParam(buf, Y_Param)) != -1) {
steppers[REVOLVER].setEnabled(true);
prepSteppingAbs(REVOLVER, smuffConfig.firstRevolverOffset + ((param)*smuffConfig.revolverSpacing), true);
runAndWait(REVOLVER);
}
if((param = getParam(buf, X_Param)) != -1) {
steppers[SELECTOR].setEnabled(true);
prepSteppingAbsMillimeter(SELECTOR, smuffConfig.firstToolOffset + (param * smuffConfig.toolSpacing));
runAndWait(SELECTOR);
}
return true;
}
bool G1(const char* msg, String buf, int serial) {
printResponse(msg, serial);
bool isMill = true;
if((param = getParam(buf, T_Param)) != -1) {
isMill = (param == 1);
}
if((param = getParam(buf, Y_Param)) != -1) {
//__debug("G1 moving Y: %d", param);
steppers[REVOLVER].setEnabled(true);
prepStepping(REVOLVER, (long)param, isMill);
}
if((param = getParam(buf, X_Param)) != -1) {
//__debug("G1 moving X: %d", param);
steppers[SELECTOR].setEnabled(true);
prepStepping(SELECTOR, (long)param, isMill, true);
}
if((param = getParam(buf, Z_Param)) != -1) {
//__debug("G1 moving Z: %d", param);
steppers[FEEDER].setEnabled(true);
prepStepping(FEEDER, (long)param, isMill);
}
runAndWait(-1);
return true;
}
bool G4(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if((param = getParam(buf, S_Param)) != -1) {
if(param > 0 && param < 500)
delay(param*1000);
}
else if((param = getParam(buf, P_Param)) != -1) {
delay(param);
}
else {
stat = false;
}
return stat;
}
bool G12(const char* msg, String buf, int serial) {
printResponse(msg, serial);
int wait = 500;
int pos1 = 20;
int pos2 = 45;
int pos0 = 110;
unsigned repeat = 0;
if((param = getParam(buf, S_Param)) != -1) {
wait = param;
}
if((param = getParam(buf, I_Param)) != -1) {
pos1 = param;
}
if((param = getParam(buf, J_Param)) != -1) {
pos2 = param;
}
if((param = getParam(buf, P_Param)) != -1) {
pos0 = param;
}
if((param = getParam(buf, R_Param)) != -1) {
repeat = param;
}
if(smuffConfig.wipeSequence[0] > 0) {
unsigned n = 1;
if(repeat > 0) {
for(n=0; n < repeat; n++) {
servo.write(pos1);
delay(wait);
servo.write(pos2);
delay(wait);
}
servo.write(pos0);
delay(100);
servo.stop();
}
else {
while(n < sizeof(smuffConfig.wipeSequence)) {
if(smuffConfig.wipeSequence[n] == -1)
break;
servo.write(smuffConfig.wipeSequence[n]);
delay(wait);
n++;
}
}
}
else {
setServoPos(10);
delay(wait);
setServoPos(110);
delay(100);
servo.stop();
}
return true;
}
bool G28(const char* msg, String buf, int serial) {
bool stat = true;
printResponse(msg, serial);
if(buf.length()==0) {
stat = moveHome(SELECTOR, false, true);
if(stat)
moveHome(REVOLVER, false, false);
}
else {
if(buf.indexOf(X_Param) != -1) {
stat = moveHome(SELECTOR, false, false);
}
if(buf.indexOf(Y_Param) != -1) {
stat = moveHome(REVOLVER, false, false);
}
}
return stat;
}
bool G90(const char* msg, String buf, int serial) {
printResponse(msg, serial);
positionMode = ABSOLUTE;
return true;
}
bool G91(const char* msg, String buf, int serial) {
printResponse(msg, serial);
positionMode = RELATIVE;
return true;
}

67
src/Gcodes.h Normal file
View File

@@ -0,0 +1,67 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _GCODES_H
#define _GCODES_H
#include <stdlib.h>
#include <Arduino.h>
typedef struct {
int code;
bool (*func)(const char* msg, String buf, int serial);
} GCodeFunctions;
extern unsigned int currentLine;
extern bool dummy(const char* msg, String buf, int serial);
extern bool M18(const char* msg, String buf, int serial);
extern bool M20(const char* msg, String buf, int serial);
extern bool M42(const char* msg, String buf, int serial);
extern bool M106(const char* msg, String buf, int serial);
extern bool M107(const char* msg, String buf, int serial);
extern bool M110(const char* msg, String buf, int serial);
extern bool M111(const char* msg, String buf, int serial);
extern bool M114(const char* msg, String buf, int serial);
extern bool M115(const char* msg, String buf, int serial);
extern bool M117(const char* msg, String buf, int serial);
extern bool M119(const char* msg, String buf, int serial);
extern bool M201(const char* msg, String buf, int serial);
extern bool M203(const char* msg, String buf, int serial);
extern bool M206(const char* msg, String buf, int serial);
extern bool M250(const char* msg, String buf, int serial);
extern bool M280(const char* msg, String buf, int serial);
extern bool M300(const char* msg, String buf, int serial);
extern bool M500(const char* msg, String buf, int serial);
extern bool M503(const char* msg, String buf, int serial);
extern bool M700(const char* msg, String buf, int serial);
extern bool M701(const char* msg, String buf, int serial);
extern bool M999(const char* msg, String buf, int serial);
extern bool M2000(const char* msg, String buf, int serial);
extern bool M2001(const char* msg, String buf, int serial);
extern bool G0(const char* msg, String buf, int serial);
extern bool G1(const char* msg, String buf, int serial);
extern bool G4(const char* msg, String buf, int serial);
extern bool G12(const char* msg, String buf, int serial);
extern bool G28(const char* msg, String buf, int serial);
extern bool G90(const char* msg, String buf, int serial);
extern bool G91(const char* msg, String buf, int serial);
#endif

55
src/SMUFF.CFG Normal file
View File

@@ -0,0 +1,55 @@
{
"Serial1Baudrate": 250000,
"Serial2Baudrate": 38400,
"SerialDueBaudrate": 56700,
"ToolCount": 5,
"BowdenLength": 620.0,
"LCDContrast": 190,
"I2CAddress": 0x88,
"MenuAutoClose": 20,
"FanSpeed": 50,
"DelayBetweenPulses": false,
"PowerSaveTimeout": 300,
"DuetDirect": true,
"Selector": {
"Offset": 0.5,
"Spacing": 21.0,
"StepsPerMillimeter": 800,
"Acceleration": 900,
"MaxSpeed": 100,
"InvertDir": false,
"EndstopTrigger": 1
},
"Revolver": {
"StepsPerRevolution": 9600,
"Offset": 1275,
"Acceleration": 6000,
"MaxSpeed": 1000,
"ResetBeforeFeed": true,
"HomeAfterFeed": true,
"InvertDir": false,
"EndstopTrigger": 1
},
"Feeder": {
"ExternalControl": true,
"StepsPerMillimeter": 410,
"Acceleration": 1000,
"MaxSpeed": 50,
"InsertSpeed": 1000,
"ReinforceLength": 2.0,
"UnloadRetract": 0,
"UnloadPushback": 0,
"PushbackDelay": 2.0,
"InvertDir": true,
"EndstopTrigger": 1
},
"Materials": {
"Tool0": "PLA Green",
"Tool1": "PLA White",
"Tool2": "PLA Red",
"Tool3": "PLA Black",
"Tool4": "PLA Silver"
}
}

659
src/SMuFF.cpp Normal file
View File

@@ -0,0 +1,659 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "SMuFF.h"
#include "Config.h"
#include "ZTimerLib.h"
#include "ZStepperLib.h"
#include "ZServo.h"
ZStepper steppers[NUM_STEPPERS];
ZTimer stepperTimer;
ZServo servo;
U8G2_ST7565_64128N_F_4W_HW_SPI display(U8G2_R2, /* cs=*/ DSP_CS_PIN, /* dc=*/ DSP_DC_PIN, /* reset=*/ DSP_RESET_PIN);
Encoder encoder(ENCODER1_PIN, ENCODER2_PIN);
volatile byte nextStepperFlag = 0;
volatile byte remainingSteppersFlag = 0;
volatile unsigned long lastEncoderButtonTime = 0;
bool testMode = false;
int toolSelections[MAX_TOOLS];
unsigned long pwrSaveTime;
bool isPwrSave = false;
String serialBuffer0, serialBuffer2, serialBuffer9;
String mainList;
String toolsList;
String offsetsList;
String swapList;
String traceSerial2;
char tmp[128];
extern char _title[128];
extern int swapTools[MAX_TOOLS];
void isrTimerHandler(); // forward declarations ... make every compiler happy
void setPwrSave(int state);
bool checkUserMessage();
void showMainMenu();
void showToolsMenu();
void showOffsetsMenu();
void showSwapMenu();
void changeOffset(int index);
void drawOffsetPosition(int index);
void drawSwapTool(int from, int with);
uint8_t swapTool(uint8_t index);
void overrideStepX() {
STEP_HIGH_X
// if(smuffConfig.delayBetweenPulses) __asm__ volatile ("nop");
STEP_LOW_X
}
void overrideStepY() {
STEP_HIGH_Y
// if(smuffConfig.delayBetweenPulses) __asm__ volatile ("nop");
STEP_LOW_Y
}
void overrideStepZ() {
STEP_HIGH_Z
// if(smuffConfig.delayBetweenPulses) __asm__ volatile ("nop");
STEP_LOW_Z
}
void endstopYevent() {
//__debug("Endstop Revolver: %d", steppers[REVOLVER].getStepPosition());
}
void endstopZevent() {
//__debug("Endstop Revolver: %d", steppers[REVOLVER].getStepPosition());
}
void setup() {
serialBuffer0.reserve(80);
serialBuffer2.reserve(80);
serialBuffer9.reserve(80);
Serial.begin(57600); // set fixed baudrate until config file was read
setupDisplay();
readConfig();
Serial.begin(smuffConfig.serial1Baudrate);
Serial2.begin(smuffConfig.serial2Baudrate);
steppers[SELECTOR] = ZStepper(SELECTOR, (char*)"Selector", X_STEP_PIN, X_DIR_PIN, X_ENABLE_PIN, smuffConfig.acceleration_X, smuffConfig.maxSpeed_X);
steppers[SELECTOR].setEndstop(X_END_PIN, smuffConfig.endstopTrigger_X, ZStepper::MIN);
steppers[SELECTOR].stepFunc = overrideStepX;
steppers[SELECTOR].setMaxStepCount(smuffConfig.maxSteps_X);
steppers[SELECTOR].setStepsPerMM(smuffConfig.stepsPerMM_X);
steppers[SELECTOR].setInvertDir(smuffConfig.invertDir_X);
steppers[REVOLVER] = ZStepper(REVOLVER, (char*)"Revolver", Y_STEP_PIN, Y_DIR_PIN, Y_ENABLE_PIN, smuffConfig.acceleration_Y, smuffConfig.maxSpeed_Y);
steppers[REVOLVER].setEndstop(Y_END_PIN, smuffConfig.endstopTrigger_Y, ZStepper::ORBITAL);
steppers[REVOLVER].stepFunc = overrideStepY;
steppers[REVOLVER].setMaxStepCount(smuffConfig.stepsPerRevolution_Y);
steppers[REVOLVER].endstopFunc = endstopYevent;
steppers[REVOLVER].setInvertDir(smuffConfig.invertDir_Y);
steppers[FEEDER] = ZStepper(FEEDER, (char*)"Feeder", Z_STEP_PIN, Z_DIR_PIN, Z_ENABLE_PIN, smuffConfig.acceleration_Z, smuffConfig.maxSpeed_Z);
steppers[FEEDER].setEndstop(Z_END_PIN, smuffConfig.endstopTrigger_Z, ZStepper::MIN);
steppers[FEEDER].stepFunc = overrideStepZ;
steppers[FEEDER].setStepsPerMM(smuffConfig.stepsPerMM_Z);
steppers[FEEDER].endstopFunc = endstopZevent;
steppers[FEEDER].setInvertDir(smuffConfig.invertDir_Z);
stepperTimer.setupTimer(ZTimer::TIMER4, ZTimer::PRESCALER1);
stepperTimer.setupTimerHook(isrTimerHandler);
getEepromData();
//__debug("DONE reading EEPROM");
char menu[256];
sprintf_P(menu, P_MenuItemBack);
strcat_P(menu, P_MenuItems);
mainList = String(menu);
//__debug("DONE setting Main menu");
sprintf_P(menu, P_MenuItemBack);
strcat_P(menu, P_OfsMenuItems);
offsetsList = String(menu);
for(int i=0; i < NUM_STEPPERS; i++) {
steppers[i].runAndWaitFunc = runAndWait;
steppers[i].runNoWaitFunc = runNoWait;
steppers[i].setEnabled(true);
}
//__debug("DONE enabling steppers");
for(int i=0; i < MAX_TOOLS; i++) {
swapTools[i] = i;
}
servo.attach(SERVO1_PIN);
pinMode(FAN_PIN, OUTPUT);
if(smuffConfig.fanSpeed > 0 && smuffConfig.fanSpeed <= 255)
analogWrite(FAN_PIN, smuffConfig.fanSpeed);
if(smuffConfig.i2cAddress != 0) {
Wire.begin(smuffConfig.i2cAddress);
Wire.onReceive(wireReceiveEvent);
}
//__debug("DONE I2C init");
resetRevolver();
//__debug("DONE reset Revolver");
//servo.setServoPos(0);
sendStartResponse(0);
//sendStartResponse(2);
pwrSaveTime = millis();
}
void setupToolsMenu() {
char menu[256];
sprintf_P(menu, P_MenuItemBack);
memset(toolSelections, 0, sizeof(int)*MAX_TOOLS);
int n = 0;
for(int i=0; i< smuffConfig.toolCount; i++) {
if(i == toolSelected)
continue;
toolSelections[n] = i;
sprintf_P(tmp, P_ToolMenu, i);
strcat(menu, tmp);
strcat(menu, (char*)"\n");
n++;
}
menu[strlen(menu)-1] = '\0';
toolsList = String(menu);
}
void setupSwapMenu() {
char menu[256];
sprintf_P(menu, P_MenuItemBack);
sprintf_P(tmp, P_SwapReset);
strcat(menu, tmp);
for(int i=0; i< smuffConfig.toolCount; i++) {
sprintf_P(tmp, P_SwapMenu, i, swapTools[i]);
strcat(menu, tmp);
strcat(menu, (char*)"\n");
}
menu[strlen(menu)-1] = '\0';
swapList = String(menu);
}
void setNextInterruptInterval() {
unsigned int minDuration = 65535;
for(int i = 0; i < NUM_STEPPERS; i++) {
if((_BV(i) & remainingSteppersFlag) && steppers[i].getDuration() < minDuration ) {
minDuration = steppers[i].getDuration();
}
}
nextStepperFlag = 0;
for(int i = 0; i < NUM_STEPPERS; i++) {
if ( (_BV(i) & remainingSteppersFlag) && steppers[i].getDuration() == minDuration )
nextStepperFlag |= _BV(i);
}
if (remainingSteppersFlag == 0) {
stepperTimer.setOCRxA(65500);
}
//__debug("minDuration: %d", minDuration);
stepperTimer.setNextInterruptInterval(minDuration);
}
void isrTimerHandler() {
unsigned int tmp = stepperTimer.getOCRxA();
stepperTimer.setOCRxA(65500);
for (int i = 0; i < NUM_STEPPERS; i++) {
if(!(_BV(i) & remainingSteppersFlag))
continue;
if(!(nextStepperFlag & _BV(i))) {
steppers[i].setDuration(steppers[i].getDuration() - tmp);
continue;
}
steppers[i].handleISR();
if(steppers[i].getMovementDone())
remainingSteppersFlag &= ~_BV(i);
}
//__debug("ISR(): %d", remainingSteppersFlag);
setNextInterruptInterval();
}
void runNoWait(int index) {
if(index != -1)
remainingSteppersFlag |= _BV(index);
setNextInterruptInterval();
}
void runAndWait(int index) {
runNoWait(index);
while(remainingSteppersFlag);
}
static int lastTurn;
static bool showMenu = false;
static bool lastZEndstopState = 0;
static unsigned long lastDisplayRefresh = 0;
void loop() {
if(feederEndstop() != lastZEndstopState) {
lastZEndstopState = feederEndstop();
bool state = feederEndstop();
setSignalPort(FEEDER_SIGNAL, state);
delay(200);
setSignalPort(FEEDER_SIGNAL, !state);
delay(200);
setSignalPort(FEEDER_SIGNAL, state);
}
//__debug("Mem: %d", freeMemory());
if(!checkUserMessage()) {
if(!isPwrSave) {
if(millis()-lastDisplayRefresh > 500) { // refresh display every 500ms
display.firstPage();
do {
drawLogo();
drawStatus();
} while(display.nextPage());
lastDisplayRefresh = millis();
}
}
}
else {
if(millis()-userMessageTime > USER_MESSAGE_RESET*1000) {
displayingUserMessage = false;
}
}
int button = digitalRead(ENCODER_BUTTON_PIN);
if(button == LOW && isPwrSave) {
setPwrSave(0);
}
else {
int turn = encoder.read();
if(turn % ENCODER_DELAY_MENU == 0) {
if(!showMenu && turn != lastTurn) {
if(isPwrSave) {
setPwrSave(0);
}
else {
displayingUserMessage = false;
showMenu = true;
if(turn < lastTurn) {
showMainMenu();
}
else {
showToolsMenu();
}
turn = encoder.read();
showMenu = false;
}
}
lastTurn = turn;
}
}
delay(10);
if((millis() - pwrSaveTime)/1000 >= (unsigned long)smuffConfig.powerSaveTimeout && !isPwrSave) {
//__debug("Power save mode after %d seconds (%d)", (millis() - pwrSaveTime)/1000, smuffConfig.powerSaveTimeout);
setPwrSave(1);
}
}
void setPwrSave(int state) {
display.setPowerSave(state);
isPwrSave = state == 1;
if(!isPwrSave) {
delay(2000);
pwrSaveTime = millis();
}
}
bool checkUserMessage() {
int button = digitalRead(ENCODER_BUTTON_PIN);
if(button == LOW && displayingUserMessage) {
displayingUserMessage = false;
}
if(millis()-userMessageTime > USER_MESSAGE_RESET*1000) {
displayingUserMessage = false;
//__debug("%ld", (millis()-userMessageTime)/1000);
}
return displayingUserMessage;
}
void showMainMenu() {
bool stopMenu = false;
unsigned int startTime = millis();
uint8_t current_selection = 0;
sprintf_P(_title, P_TitleMainMenu);
do {
resetAutoClose();
while(checkUserMessage());
current_selection = display.userInterfaceSelectionList(_title, current_selection, mainList.c_str());
if(current_selection == 0)
return;
else {
switch(current_selection) {
case 1:
stopMenu = true;
break;
case 2:
moveHome(SELECTOR);
moveHome(REVOLVER, true, false);
startTime = millis();
break;
case 3:
steppers[SELECTOR].setEnabled(false);
steppers[REVOLVER].setEnabled(false);
steppers[FEEDER].setEnabled(false);
startTime = millis();
break;
case 4:
showOffsetsMenu();
break;
case 5:
loadFilament();
startTime = millis();
break;
case 6:
unloadFilament();
startTime = millis();
break;
case 7:
showSwapMenu();
break;
}
}
if (millis() - startTime > (unsigned long)smuffConfig.menuAutoClose * 1000) {
stopMenu = true;
}
} while(!stopMenu);
}
void drawSwapTool(int from, int with) {
sprintf_P(tmp, P_SwapToolDialog, from, with);
drawUserMessage(String(tmp));
}
uint8_t swapTool(uint8_t index) {
int turn = encoder.read();
int lastTurn = 0;
int lastButton = HIGH;
int button;
uint8_t ndx = 0;
while((button = digitalRead(ENCODER_BUTTON_PIN)) == LOW)
delay(20);
drawSwapTool(index, ndx);
while(1) {
lastButton = button;
button = digitalRead(ENCODER_BUTTON_PIN);
if(button == LOW && lastButton == HIGH)
break;
turn = encoder.read();
if(turn % ENCODER_DELAY_MENU == 0) {
if(turn == lastTurn)
continue;
if(turn < lastTurn)
ndx -= 1;
else
ndx += 1;
if(ndx >= smuffConfig.toolCount)
ndx = 0;
if(ndx < 0)
ndx = smuffConfig.toolCount-1;
lastTurn = turn;
drawSwapTool(index, ndx);
}
}
return ndx;
}
void showSwapMenu() {
bool stopMenu = false;
uint8_t current_selection = 0;
sprintf_P(_title, P_TitleSwapMenu);
do {
setupSwapMenu();
resetAutoClose();
while(checkUserMessage());
current_selection = display.userInterfaceSelectionList(_title, current_selection, swapList.c_str());
if(current_selection == 0)
return;
else if(current_selection == 1) {
stopMenu = true;
}
else if(current_selection == 2) {
for(int i=0; i < MAX_TOOLS; i++) {
swapTools[i] = i;
}
}
else {
uint8_t tool = swapTool(current_selection-3);
uint8_t tmp = swapTools[current_selection-3];
swapTools[current_selection-3] = tool;
swapTools[tool] = tmp;
}
} while(!stopMenu);
}
void showOffsetsMenu() {
bool stopMenu = false;
unsigned int startTime = millis();
uint8_t current_selection = 0;
sprintf_P(_title, P_TitleOffsetsMenu);
do {
resetAutoClose();
while(checkUserMessage());
current_selection = display.userInterfaceSelectionList(_title, current_selection, offsetsList.c_str());
if(current_selection == 0)
return;
else {
switch(current_selection) {
case 1:
stopMenu = true;
break;
case 2:
changeOffset(SELECTOR);
startTime = millis();
break;
case 3:
changeOffset(REVOLVER);
startTime = millis();
break;
}
}
if (millis() - startTime > (unsigned long)smuffConfig.menuAutoClose * 1000) {
stopMenu = true;
}
} while(!stopMenu);
}
void changeOffset(int index) {
int turn = encoder.read();
int lastTurn = 0;
int lastButton = HIGH;
int button;
int steps = (smuffConfig.stepsPerRevolution_Y / 360);
float stepsF = 0.1f;
while((button = digitalRead(ENCODER_BUTTON_PIN)) == LOW)
delay(20);
moveHome(index);
long pos = steppers[index].getStepPosition();
float posF = steppers[index].getStepPositionMM();
drawOffsetPosition(index);
while(1) {
lastButton = button;
button = digitalRead(ENCODER_BUTTON_PIN);
if(button == LOW && lastButton == HIGH)
break;
turn = encoder.read();
if(turn == lastTurn)
continue;
if(turn < lastTurn) {
pos = -steps;
posF = -stepsF;
}
else {
pos = steps;
posF = stepsF;
}
lastTurn = turn;
if(index == REVOLVER) {
prepSteppingRel(index, pos, true);
}
if(index == SELECTOR) {
prepSteppingRelMillimeter(SELECTOR, posF, true);
}
runAndWait(index);
drawOffsetPosition(index);
}
}
void drawOffsetPosition(int index) {
if(index == REVOLVER) {
sprintf(tmp, "%s\n%ld", steppers[index].getDescriptor(), steppers[index].getStepPosition());
}
if(index == SELECTOR) {
sprintf(tmp, "%s\n%s", steppers[index].getDescriptor(), String(steppers[index].getStepPositionMM()).c_str());
}
drawUserMessage(String(tmp));
}
void showToolsMenu() {
bool stopMenu = false;
unsigned int startTime = millis();
uint8_t current_selection = 0;
sprintf_P(_title, P_TitleToolsMenu);
do {
setupToolsMenu();
resetAutoClose();
while(checkUserMessage());
uint8_t startPos = toolSelected == 255 ? 0 : toolSelected+1;
current_selection = display.userInterfaceSelectionList(_title, startPos, toolsList.c_str());
if(current_selection <= 1)
stopMenu = true;
else {
int tool = toolSelections[current_selection-2];
if(!smuffConfig.duetDirect) {
selectTool(tool);
}
else {
selectTool(tool);
// TODO: do tool change using Duet3D
// not yet possible due to Duet3D is being blocked waiting for endstop
/*
sprintf(tmp, "T%d\n", tool);
Serial2.print(tmp);
*/
}
startTime = millis();
}
if (millis() - startTime > (unsigned long)smuffConfig.menuAutoClose * 1000) {
stopMenu = true;
}
} while(!stopMenu);
}
void resetAutoClose() {
lastEncoderButtonTime = millis();
}
bool checkAutoClose() {
//__debug("Home: %ld", millis()-lastEncoderButtonTime);
if (millis() - lastEncoderButtonTime >= (unsigned long)smuffConfig.menuAutoClose*1000) {
return true;
}
return false;
}
void serialEvent() {
while (Serial.available()) {
char in = (char)Serial.read();
if (in == '\n') {
//__debug("Received: %s", serialBuffer0.c_str());
parseGcode(serialBuffer0, 0);
serialBuffer0 = "";
}
else
serialBuffer0 += in;
}
}
void serialEvent2() {
while (Serial2.available()) {
char in = (char)Serial2.read();
Serial.write(in);
if (in == '\n') {
parseGcode(serialBuffer2, 2);
traceSerial2 = String(serialBuffer2);
serialBuffer2 = "";
}
else
serialBuffer2 += in;
}
}
void wireReceiveEvent(int numBytes) {
while (Wire.available()) {
char in = (char)Wire.read();
if (in == '\n') {
parseGcode(serialBuffer9, 9);
serialBuffer9 = "";
}
else
serialBuffer9 += in;
}
}

188
src/SMuFF.h Normal file
View File

@@ -0,0 +1,188 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _SMUFF_H
#define _SMUFF_H
#define DEBUG 1
#include "Config.h"
#include "Strings.h"
#include "GCodes.h"
#include "Encoder.h"
#include <EEPROM.h>
#include <Wire.h>
#include <SD.h>
#include "U8g2lib.h"
#include "MemoryFree.h"
#define FEEDER_SIGNAL 1
#define SELECTOR_SIGNAL 2
#define REVOLVER_SIGNAL 3
#define LED_SIGNAL 4
typedef enum {
ABSOLUTE,
RELATIVE
} PositionMode;
typedef struct {
int toolCount = 5;
float firstToolOffset = FIRST_TOOL_OFFSET;
float toolSpacing = TOOL_SPACING;
int firstRevolverOffset = FIRST_REVOLVER_OFFSET;
int revolverSpacing = REVOLVER_SPACING;
long stepsPerMM_X = X_STEPS_PER_MM;
long maxSteps_X = 68000;
int maxSpeed_X = 10;
int acceleration_X = 510;
bool invertDir_X = false;
int endstopTrigger_X = HIGH;
long stepsPerRevolution_Y= 9600;
long maxSteps_Y = 9600;
int maxSpeed_Y = 800;
int acceleration_Y = 2000;
bool resetBeforeFeed_Y = true;
bool invertDir_Y = false;
int endstopTrigger_Y = HIGH;
bool externalControl_Z = false;
long stepsPerMM_Z = Z_STEPS_PER_MM;
int maxSpeed_Z = 10;
int insertSpeed_Z = 1000;
int acceleration_Z = 300;
bool invertDir_Z = false;
int endstopTrigger_Z = LOW;
float unloadRetract = -20.0f;
float unloadPushback = 5.0f;
float pushbackDelay = 1.5f;
float reinforceLength = 3.0f;
bool homeAfterFeed = true;
float bowdenLength = 400.0f;
int i2cAddress = 0x58;
int lcdContrast = DSP_CONTRAST;
int menuAutoClose = 20;
bool delayBetweenPulses = false;
unsigned long serial1Baudrate = 57600;
unsigned long serial2Baudrate = 57600;
unsigned long serialDueBaudrate = 57600;
bool duetDirect = false;
int fanSpeed = 0;
char materials[MAX_TOOLS][20];
long powerSaveTimeout = 300;
char unloadCommand[80] = {{ 0 }};
int wipeSequence[20] = { 150,20,45,20,45,20,45,20,45,20,45,20,45,20,45,20,45,20,110,-1 };
} SMuFFConfig;
extern U8G2_ST7565_64128N_F_4W_HW_SPI display;
extern Encoder encoder;
extern SMuFFConfig smuffConfig;
extern GCodeFunctions gCodeFuncsM[];
extern GCodeFunctions gCodeFuncsG[];
extern const char brand[];
extern volatile byte nextStepperFlag;
extern volatile byte remainingSteppersFlag;
extern volatile unsigned long lastEncoderButtonTime;
extern char buf[];
extern byte toolSelected;
extern PositionMode positionMode;
extern String serialBuffer0, serialBuffer2, serialBuffer3, serialBuffer9, traceSerial2;
extern bool displayingUserMessage;
extern unsigned int userMessageTime;
extern bool testMode;
extern bool feederJamed;
extern bool parserBusy;
extern bool isPwrSave;
extern void setupDisplay();
extern void drawLogo();
extern void drawStatus();
extern void drawSelectingMessage();
extern void drawUserMessage(String message);
extern void drawSDStatus(int stat);
extern void resetDisplay();
extern bool selectorEndstop();
extern bool revolverEndstop();
extern bool feederEndstop();
extern bool showFeederLoadedMessage();
extern bool showFeederLoadMessage();
extern bool showFeederFailedMessage(int state);
extern int showDialog(PGM_P title, PGM_P message, PGM_P addMessage, PGM_P buttons);
extern bool moveHome(int index, bool showMessage = true, bool checkFeeder = true);
extern bool loadFilament(bool showMessage = true);
extern bool unloadFilament();
extern void runAndWait(int index);
extern void runNoWait(int index);
extern bool selectTool(int ndx, bool showMessage = true);
extern void setStepperSteps(int index, long steps, bool ignoreEndstop);
extern void prepSteppingAbs(int index, long steps, bool ignoreEndstop = false);
extern void prepSteppingAbsMillimeter(int index, float millimeter, bool ignoreEndstop = false);
extern void prepSteppingRel(int index, long steps, bool ignoreEndstop = false);
extern void prepSteppingRelMillimeter(int index, float millimeter, bool ignoreEndstop = false);
extern void resetRevolver();
extern void serialEvent();
extern void serialEvent2();
extern void wireReceiveEvent(int numBytes);
extern void beep(int count);
extern void longBeep(int count);
extern void userBeep();
extern void setSignalPort(int port, bool state);
extern void signalNoTool();
extern void signalLoadFilament();
extern void signalUnloadFilament();
extern void signalSelectorBusy();
extern void signalSelectorReady();
extern bool setServoPos(int degree);
extern void getEepromData();
extern void readConfig();
extern bool checkAutoClose();
extern void resetAutoClose();
extern void listDir(File root, int numTabs, int serial);
extern void setPwrSave(int state);
extern void __debug(const char* fmt, ...);
extern void printEndstopState(int serial);
extern void printPos(int index, int serial);
extern void printAcceleration(int serial);
extern void printSpeeds(int serial);
extern void sendGList(int serial);
extern void sendMList(int serial);
extern void sendToolResponse(int serial);
extern void sendStartResponse(int serial);
extern void sendOkResponse(int serial);
extern void sendErrorResponse(int serial, const char* msg = NULL);
extern void sendErrorResponseP(int serial, const char* msg = NULL);
extern void parseGcode(String serialBuffer, int serial);
extern bool parse_G(String buf, int serial);
extern bool parse_M(String buf, int serial);
extern bool parse_T(String buf, int serial);
extern int getParam(String buf, char* token);
extern bool getParamString(String buf, char* token, char* dest, int bufLen);
extern void prepStepping(int index, long param, bool Millimeter = true, bool ignoreEndstop = false);
extern void saveSettings(int serial);
extern void reportSettings(int serial);
extern void printResponse(const char* response, int serial);
extern void printResponseP(const char* response, int serial);
extern void printOffsets(int serial);
#endif

113
src/SMuFFBitmaps.h Normal file
View File

@@ -0,0 +1,113 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _SMUFF_BITMAPS_H
#define _SMUFF_BITMAPS_H
#define logo_width 128
#define logo_height 64
static const unsigned char logo_bits[] PROGMEM = {
0x00, 0x00, 0xE0, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xE0, 0x07, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0xC0, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00,
0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x03, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x81, 0x03, 0x00, 0x00, 0xC0, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x60, 0x1C, 0x00,
0xFC, 0x81, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0x10, 0x70, 0x00, 0xFF, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x38, 0x0C, 0x60, 0x1C, 0xFF, 0x0F, 0x0E, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x06, 0x90, 0xFF,
0xFE, 0x1F, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1C, 0x03, 0xE8, 0xFF, 0xFE, 0x1F, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1C, 0x01, 0xE8, 0xFF, 0xFD, 0x3F, 0x1C, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x00, 0xF4, 0xFF,
0xFD, 0x7F, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x8E, 0x00, 0xF4, 0xFF, 0xFD, 0x7F, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0xFA, 0xFF, 0xFF, 0x7F, 0x30, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x01, 0xFA, 0xFF,
0xFF, 0x3F, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x01, 0xFA, 0xCF, 0xFF, 0x3F, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0xEA, 0xEF, 0xFF, 0x3F, 0x70, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0xF2, 0xFF,
0xFF, 0x9F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x04, 0xF4, 0xFF, 0xFF, 0xC7, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x0C, 0xF4, 0xFF, 0xFF, 0xF1, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0xFA, 0xFF,
0xFF, 0xFC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x20, 0xFA, 0xFF, 0x7F, 0xFF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x40, 0xFA, 0xFF, 0xFF, 0xFF, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0xFA, 0xFF,
0xFF, 0xFF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0xFB, 0xC7, 0xFF, 0xFF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xFA, 0xE3, 0xFF, 0x7F, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0xFA, 0xE3,
0xFF, 0x7F, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0xC0, 0xFF, 0xF0, 0xFF, 0x7F, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x7F, 0xF8, 0xFF, 0x3F, 0x70, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xFC,
0xFF, 0x3F, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0E, 0x00, 0xFF, 0xFD, 0xFF, 0x1F, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0xFD, 0xFF, 0x0F, 0x18, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x08, 0xFD,
0xFF, 0x07, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1C, 0x00, 0x08, 0xFA, 0x7F, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x10, 0xFA, 0x3F, 0x82, 0x4E, 0x00,
0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x10, 0xF6,
0x9F, 0xEF, 0xCC, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0x00, 0x20, 0xE4, 0xDF, 0xC8, 0xE5, 0x00, 0x0C, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x40, 0xC8, 0xC3, 0xC1, 0x71, 0x00,
0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x80, 0x30,
0x80, 0xC1, 0x73, 0xE4, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x03, 0x00, 0x21, 0x80, 0xC7, 0x7F, 0xEC, 0x7E, 0x3F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x26, 0x00, 0xCF, 0x7E, 0xC6,
0x7E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x38,
0x00, 0xCE, 0x6C, 0xC6, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1E, 0x00, 0x20, 0x00, 0xDC, 0x64, 0xC6, 0x0C, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xDC, 0x60, 0xC6,
0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00,
0xC0, 0xCE, 0x60, 0xEE, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xE0, 0x07, 0x00, 0xC0, 0xC7, 0x61, 0xFC, 0x0E, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x9F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF,
0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xF0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, };
#endif

730
src/SMuFFtools.cpp Normal file
View File

@@ -0,0 +1,730 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Module containing helper functions
*/
#include "SMuFF.h"
#include "SMuFFBitmaps.h"
#include "Config.h"
#include "ZTimerLib.h"
#include "ZStepperLib.h"
#include "ZServo.h"
extern ZStepper steppers[];
extern ZServo servo;
extern char tmp[128];
SMuFFConfig smuffConfig;
int lastEncoderTurn = 0;
byte toolSelected = -1;
bool feederJamed = false;
PositionMode positionMode = RELATIVE;
bool displayingUserMessage = false;
unsigned int userMessageTime = 0;
char _sel[128];
char _wait[128];
char _title[128];
char _msg1[256];
char _msg2[128];
char _btn[128];
int swapTools[MAX_TOOLS];
const char brand[] = VERSION_STRING;
void setupDisplay() {
display.begin(/*Select=*/ ENCODER_BUTTON_PIN, /* menu_next_pin= */ U8X8_PIN_NONE, /* menu_prev_pin= */ U8X8_PIN_NONE, /* menu_home_pin= */ U8X8_PIN_NONE);
display.enableUTF8Print();
resetDisplay();
if (smuffConfig.lcdContrast < MIN_CONTRAST || smuffConfig.lcdContrast > MAX_CONTRAST) {
smuffConfig.lcdContrast = DSP_CONTRAST;
EEPROM.put(EEPROM_CONTRAST, smuffConfig.lcdContrast);
}
display.setContrast(smuffConfig.lcdContrast);
}
void drawLogo() {
display.setBitmapMode(1);
display.drawXBMP(0, 0, logo_width, logo_height, logo_bits);
display.setFont(LOGO_FONT);
display.setFontMode(0);
display.setFontDirection(0);
display.setDrawColor(1);
display.setCursor(display.getDisplayWidth() - display.getStrWidth(brand) - 1, display.getDisplayHeight() - display.getMaxCharHeight());
display.print(brand);
}
void drawStatus() {
display.setFont(STATUS_FONT);
display.setFontMode(0);
display.setDrawColor(1);
sprintf_P(tmp, P_CurrentTool);
display.drawStr(display.getDisplayWidth() - display.getStrWidth(tmp) - 5, 14, tmp);
display.drawStr(display.getDisplayWidth() - display.getStrWidth("X") - 5, 14, (toolSelected >= 0 && toolSelected < smuffConfig.toolCount) ? String(toolSelected).c_str() : "-");
sprintf_P(tmp, P_Feed);
display.drawStr(display.getDisplayWidth() - display.getStrWidth(tmp) - 5, 34, tmp);
display.setFontMode(1);
display.setFont(SMALL_FONT);
display.setDrawColor(2);
display.drawBox(0, display.getDisplayHeight()-display.getMaxCharHeight()+2, display.getDisplayWidth(), display.getMaxCharHeight());
sprintf_P(_wait, parserBusy ? P_Busy : P_Ready);
sprintf(tmp, "M:%d | %-4s | %-5s ", freeMemory(), traceSerial2.c_str(), _wait);
display.drawStr(1, display.getDisplayHeight(), tmp);
display.setFontMode(0);
display.setDrawColor(1);
display.setFont(ICONIC_FONT);
display.drawGlyph(110, 38, feederEndstop() ? 0x41 : 0x42);
display.setFont(BASE_FONT);
}
void resetDisplay() {
display.clearDisplay();
display.setFont(BASE_FONT);
display.setFontMode(0);
display.setDrawColor(1);
}
void drawSelectingMessage(int tool) {
display.firstPage();
do {
resetDisplay();
sprintf_P(_sel, P_Selecting);
sprintf_P(_wait, P_Wait);
if(*smuffConfig.materials[tool] != 0) {
sprintf(tmp,"%s", smuffConfig.materials[tool]);
}
else {
sprintf_P(tmp, "%s%d", P_Tool, tool);
}
display.drawStr((display.getDisplayWidth() - display.getStrWidth(_sel))/2, (display.getDisplayHeight() - display.getMaxCharHeight())/2-10, _sel);
display.setFont(BASE_FONT_BIG);
display.drawStr((display.getDisplayWidth() - display.getStrWidth(tmp))/2, (display.getDisplayHeight() - display.getMaxCharHeight())/2+9, tmp);
display.setFont(BASE_FONT);
display.drawStr((display.getDisplayWidth() - display.getStrWidth(_wait))/2, (display.getDisplayHeight() - display.getMaxCharHeight())/2 + display.getMaxCharHeight()+10, _wait);
} while(display.nextPage());
}
int splitStringLines(char lines[][MAX_LINE_LENGTH], String message) {
for(int i=0; i < MAX_LINES; i++) {
memset(&lines[i], 0, MAX_LINE_LENGTH);
}
int pos2 = (message.indexOf('\n') == -1) ? message.length() : message.indexOf('\n');
int ln = 0;
do {
int len = (pos2 > MAX_LINE_LENGTH-1) ? MAX_LINE_LENGTH-1 : pos2+1;
message.substring(0, pos2).toCharArray(lines[ln], len);
if(ln >= MAX_LINES)
break;
ln++;
message = message.substring(len);
pos2 = message.indexOf('\n');
if(pos2 == -1)
break;
} while(1);
if(message.length()>0 && ln < MAX_LINES) {
int len = message.length()+1;
message.substring(0).toCharArray(lines[ln++], len);
}
return ln;
}
void drawUserMessage(String message) {
char lines[MAX_LINES][MAX_LINE_LENGTH];
int lineCnt = splitStringLines(lines, message);
if(isPwrSave) {
setPwrSave(0);
}
display.firstPage();
do {
resetDisplay();
display.setFont(BASE_FONT_BIG);
int y = (display.getDisplayHeight()-(lineCnt-1)*display.getMaxCharHeight())/2;
display.firstPage();
display.drawFrame(0, 0, display.getDisplayWidth(), display.getDisplayHeight());
do {
for(int i=0; i< lineCnt; i++) {
display.drawStr((display.getDisplayWidth() - display.getStrWidth(lines[i]))/2, y, lines[i]);
y += display.getMaxCharHeight();
}
} while(display.nextPage());
display.setFont(BASE_FONT);
} while(display.nextPage());
displayingUserMessage = true;
userMessageTime = millis();
}
void drawSDStatus(int stat) {
resetDisplay();
switch(stat) {
case SD_ERR_INIT:
sprintf_P(tmp, P_SD_InitError);
longBeep(2);
break;
case SD_ERR_NOCONFIG:
sprintf_P(tmp, P_SD_NoConfig);
longBeep(1);
break;
case SD_READING_CONFIG:
sprintf_P(tmp, P_SD_ReadingConfig);
break;
}
display.firstPage();
do {
drawLogo();
display.setCursor((display.getDisplayWidth() - display.getStrWidth(tmp))/2, display.getDisplayHeight());
display.print(tmp);
} while(display.nextPage());
}
bool selectorEndstop() {
return steppers[SELECTOR].getEndstopHit();
}
bool revolverEndstop() {
return steppers[REVOLVER].getEndstopHit();
}
bool feederEndstop() {
/*
if(smuffConfig.externalControl_Z) {
return steppers[FEEDER].getEndstopHitAlt();
}
*/
return steppers[FEEDER].getEndstopHit();
}
uint8_t u8x8_GetMenuEvent(u8x8_t *u8x8)
{
int stat = 0;
int button = digitalRead(ENCODER_BUTTON_PIN);
int turn = encoder.read();
if (button == LOW) {
delay(20);
button = digitalRead(ENCODER_BUTTON_PIN);
if (button == LOW && u8x8->debounce_state == HIGH) {
stat = U8X8_MSG_GPIO_MENU_SELECT;
resetAutoClose();
turn = encoder.read();
lastEncoderTurn = turn;
}
}
else {
if (turn != lastEncoderTurn) {
if (turn % ENCODER_DELAY == 0) {
int delta = turn < lastEncoderTurn ? -1 : 1;
lastEncoderTurn = turn;
resetAutoClose();
switch (delta)
{
case 1:
stat = U8X8_MSG_GPIO_MENU_NEXT;
break;
case -1:
stat = U8X8_MSG_GPIO_MENU_PREV;
break;
}
}
}
}
u8x8->debounce_state = button;
serialEvent();
serialEvent2();
//wireReceiveEvent(0);
if(checkAutoClose()) {
stat = U8X8_MSG_GPIO_MENU_HOME;
}
return stat;
}
bool moveHome(int index, bool showMessage, bool checkFeeder) {
if(!steppers[index].getEnabled())
steppers[index].setEnabled(true);
if(feederJamed) {
beep(4);
return false;
}
parserBusy = true;
if (checkFeeder && feederEndstop()) {
if (showMessage) {
if (!showFeederLoadedMessage()) {
parserBusy = false;
return false;
}
}
else {
if (feederEndstop()) {
unloadFilament();
}
}
}
//__debug("Stepper home");
steppers[index].home();
//__debug("DONE Stepper home");
if (index == SELECTOR) {
toolSelected = -1;
}
long pos = steppers[index].getStepPosition();
if (index == SELECTOR || index == REVOLVER) {
EEPROM.update(EEPROM_TOOL, toolSelected);
}
EEPROM.put(index * sizeof(long), pos);
parserBusy = false;
return true;
}
bool showFeederLoadedMessage() {
bool state = false;
lastEncoderButtonTime = millis();
beep(1);
int button = showDialog(P_TitleWarning, P_FeederLoaded, P_AskUnload, P_YesNoButtons);
if (button == 1) {
drawStatus();
unloadFilament();
state = true;
}
display.clearDisplay();
return state;
}
bool showFeederLoadMessage() {
bool state = false;
lastEncoderButtonTime = millis();
beep(1);
int button = showDialog(P_TitleSelected, P_SelectedTool, P_AskLoad, P_YesNoButtons);
if (button == 1) {
drawStatus();
loadFilament();
state = true;
}
display.clearDisplay();
return state;
}
bool showFeederFailedMessage(int state) {
lastEncoderButtonTime = millis();
beep(3);
showDialog(P_TitleWarning, state == 1 ? P_CantLoad : P_CantUnload, P_CheckUnit, P_OkButtonOnly);
display.clearDisplay();
return false;
}
int showDialog(PGM_P title, PGM_P message, PGM_P addMessage, PGM_P buttons) {
if(isPwrSave) {
setPwrSave(0);
}
sprintf_P(_title, title);
sprintf_P(_msg1, message);
sprintf_P(_msg2, addMessage);
sprintf_P(_btn, buttons);
return display.userInterfaceMessage(_title, _msg1, _msg2, _btn);
}
void signalNoTool() {
userBeep();
sprintf_P(_msg1, P_NoTool);
strcat_P(_msg1, P_Aborting);
drawUserMessage(_msg1);
}
bool loadFilament(bool showMessage) {
if (toolSelected == 255) {
signalNoTool();
return false;
}
if(smuffConfig.externalControl_Z) {
resetRevolver();
signalLoadFilament();
return true;
}
parserBusy = true;
if(!steppers[FEEDER].getEnabled())
steppers[FEEDER].setEnabled(true);
if(smuffConfig.resetBeforeFeed_Y)
resetRevolver();
int n = 100;
unsigned int curSpeed = steppers[FEEDER].getMaxSpeed();
steppers[FEEDER].setMaxSpeed(smuffConfig.insertSpeed_Z);
while (!feederEndstop()) {
prepSteppingRelMillimeter(FEEDER, 2.5, true);
runAndWait(FEEDER);
if (n == 50) {
resetRevolver();
prepSteppingRelMillimeter(FEEDER, -15.0, true);
runAndWait(FEEDER);
}
if (n <= 0) {
if (showMessage)
showFeederFailedMessage(1);
steppers[FEEDER].setMaxSpeed(curSpeed);
feederJamed = true;
parserBusy = false;
return false;
}
n--;
}
feederJamed = false;
steppers[FEEDER].setMaxSpeed(curSpeed);
prepSteppingRelMillimeter(FEEDER, smuffConfig.bowdenLength*.95, true);
runAndWait(FEEDER);
steppers[FEEDER].setMaxSpeed(smuffConfig.insertSpeed_Z);
prepSteppingRelMillimeter(FEEDER, smuffConfig.bowdenLength*.05, true);
runAndWait(FEEDER);
if(smuffConfig.reinforceLength > 0) {
resetRevolver();
prepSteppingRelMillimeter(FEEDER, smuffConfig.reinforceLength, true);
runAndWait(FEEDER);
}
steppers[FEEDER].setMaxSpeed(curSpeed);
EEPROM.put(EEPROM_FEEDER_POS, steppers[FEEDER].getStepPosition());
if(smuffConfig.homeAfterFeed)
steppers[REVOLVER].home();
parserBusy = false;
return true;
}
bool unloadFilament() {
if (toolSelected == 255) {
signalNoTool();
return false;
}
if(smuffConfig.externalControl_Z) {
signalUnloadFilament();
return true;
}
parserBusy = true;
if(!steppers[FEEDER].getEnabled())
steppers[FEEDER].setEnabled(true);
if(smuffConfig.resetBeforeFeed_Y)
resetRevolver();
unsigned int curSpeed = steppers[FEEDER].getMaxSpeed();
steppers[FEEDER].setEndstopState(!steppers[FEEDER].getEndstopState());
if(smuffConfig.unloadRetract != 0) {
prepSteppingRelMillimeter(FEEDER, smuffConfig.unloadRetract);
runAndWait(FEEDER);
if(smuffConfig.unloadPushback != 0) {
steppers[FEEDER].setMaxSpeed(smuffConfig.insertSpeed_Z);
prepSteppingRelMillimeter(FEEDER, smuffConfig.unloadPushback);
runAndWait(FEEDER);
delay(smuffConfig.pushbackDelay*1000);
steppers[FEEDER].setMaxSpeed(curSpeed);
}
}
prepSteppingRelMillimeter(FEEDER, -(smuffConfig.bowdenLength*3));
runAndWait(FEEDER);
steppers[FEEDER].setMaxSpeed(smuffConfig.insertSpeed_Z);
int n = 200;
do {
prepSteppingRelMillimeter(FEEDER, -20.0, true);
runAndWait(FEEDER);
if(!feederEndstop()) {
if((n > 0 && n < 200) && n % 50 == 0) {
resetRevolver();
prepSteppingRelMillimeter(FEEDER, 15.0, true);
runAndWait(FEEDER);
}
if (n <= 0) {
showFeederFailedMessage(0);
steppers[FEEDER].setMaxSpeed(curSpeed);
feederJamed = true;
parserBusy = false;
return false;
}
}
n--;
} while (!feederEndstop());
feederJamed = false;
steppers[FEEDER].setMaxSpeed(curSpeed);
steppers[FEEDER].setEndstopState(!steppers[FEEDER].getEndstopState());
steppers[FEEDER].setStepPosition(0);
EEPROM.put(EEPROM_FEEDER_POS, steppers[FEEDER].getStepPosition());
parserBusy = false;
return true;
}
bool selectTool(int ndx, bool showMessage) {
ndx = swapTools[ndx];
if(feederJamed) {
beep(4);
sprintf_P(_msg1, P_FeederJamed);
strcat_P(_msg1, P_Aborting);
drawUserMessage(_msg1);
return false;
}
signalSelectorBusy();
if(toolSelected == ndx) {
if(!smuffConfig.externalControl_Z) {
userBeep();
sprintf_P(_msg1, P_ToolAlreadySet);
drawUserMessage(_msg1);
}
if(smuffConfig.externalControl_Z) {
//resetRevolver();
signalSelectorReady();
}
return false;
}
if(!steppers[SELECTOR].getEnabled())
steppers[SELECTOR].setEnabled(true);
if (showMessage) {
while(feederEndstop()) {
if (!showFeederLoadedMessage())
return false;
}
}
else {
if (!smuffConfig.externalControl_Z && feederEndstop()) {
unloadFilament();
}
else if (smuffConfig.externalControl_Z && feederEndstop()) {
// TODO: Signal Duet3D to retract 2mm
beep(4);
char buf[] = {'Y'};
while(feederEndstop()) {
M18("M18", buf, 0);
showFeederFailedMessage(0);
if(smuffConfig.unloadCommand != NULL && strlen(smuffConfig.unloadCommand) > 0) {
Serial2.print(smuffConfig.unloadCommand);
Serial2.print("\n");
__debug("Feeder jamed, sent unload command '%s'\n", smuffConfig.unloadCommand);
}
}
}
}
//__debug("Selecting tool: %d", ndx);
parserBusy = true;
drawSelectingMessage(ndx);
prepSteppingAbsMillimeter(SELECTOR, smuffConfig.firstToolOffset + (ndx * smuffConfig.toolSpacing));
remainingSteppersFlag |= _BV(SELECTOR);
if(!smuffConfig.resetBeforeFeed_Y) {
prepSteppingAbs(REVOLVER, smuffConfig.firstRevolverOffset + (ndx *smuffConfig.revolverSpacing), true);
remainingSteppersFlag |= _BV(REVOLVER);
}
runAndWait(-1);
toolSelected = ndx;
EEPROM.put(EEPROM_TOOL, toolSelected);
for (int i = 0; i < NUM_STEPPERS; i++) {
EEPROM.put(i * sizeof(long), steppers[i].getStepPosition());
}
if (!smuffConfig.externalControl_Z && showMessage) {
showFeederLoadMessage();
}
if(smuffConfig.externalControl_Z) {
resetRevolver();
signalSelectorReady();
}
if(testMode)
Serial2.print("T"); Serial2.println(ndx);
parserBusy = false;
return true;
}
void resetRevolver() {
//__debug("resetting revolver");
moveHome(REVOLVER, false, false);
//__debug("DONE resetting revolver");
if (toolSelected > -1) {
prepSteppingAbs(REVOLVER, smuffConfig.firstRevolverOffset + (toolSelected*smuffConfig.revolverSpacing), true);
runAndWait(REVOLVER);
}
}
void setStepperSteps(int index, long steps, bool ignoreEndstop) {
if (steps != 0)
steppers[index].prepareMovement(steps, ignoreEndstop);
}
void prepSteppingAbs(int index, long steps, bool ignoreEndstop) {
long pos = steppers[index].getStepPosition();
long _steps = steps - pos;
setStepperSteps(index, _steps, ignoreEndstop);
}
void prepSteppingAbsMillimeter(int index, float millimeter, bool ignoreEndstop) {
unsigned int stepsPerMM = steppers[index].getStepsPerMM();
long steps = (long)((float)millimeter * stepsPerMM);
long pos = steppers[index].getStepPosition();
setStepperSteps(index, steps - pos, ignoreEndstop);
}
void prepSteppingRel(int index, long steps, bool ignoreEndstop) {
setStepperSteps(index, steps, ignoreEndstop);
}
void prepSteppingRelMillimeter(int index, float millimeter, bool ignoreEndstop) {
unsigned int stepsPerMM = steppers[index].getStepsPerMM();
long steps = (long)((float)millimeter * stepsPerMM);
setStepperSteps(index, steps, ignoreEndstop);
}
void printEndstopState(int serial) {
sprintf(tmp, "Selector: %s\tRevolver: %s\tFeeder: %s\n",
selectorEndstop() ? "triggered" : "open",
revolverEndstop() ? "triggered" : "open",
feederEndstop() ? "triggered" : "open");
printResponse(tmp, serial);
}
void printSpeeds(int serial) {
sprintf_P(tmp, P_AccelSpeed,
String(steppers[SELECTOR].getMaxSpeed()).c_str(),
String(steppers[REVOLVER].getMaxSpeed()).c_str(),
smuffConfig.externalControl_Z ? "external" : String(steppers[FEEDER].getMaxSpeed()).c_str());
printResponse(tmp, serial);
}
void printAcceleration(int serial) {
sprintf_P(tmp, P_AccelSpeed,
String(steppers[SELECTOR].getAcceleration()).c_str(),
String(steppers[REVOLVER].getAcceleration()).c_str(),
smuffConfig.externalControl_Z ? "external" : String(steppers[FEEDER].getAcceleration()).c_str());
printResponse(tmp, serial);
}
void printOffsets(int serial) {
sprintf_P(tmp, P_AccelSpeed,
String((int)(smuffConfig.firstToolOffset*10)).c_str(),
String(smuffConfig.firstRevolverOffset).c_str(),
"--");
printResponse(tmp, serial);
}
void printPos(int index, int serial) {
sprintf(buf, "Pos. '%s': %ld\n", steppers[index].getDescriptor(), steppers[index].getStepPosition());
printResponse(buf, serial);
}
void beep(int count) {
for (int i = 0; i < count; i++) {
tone(BEEPER_PIN, BEEPER_FREQUENCY, BEEPER_DURATION);
delay(BEEPER_DURATION*2);
}
}
void longBeep(int count) {
for (int i = 0; i < count; i++) {
tone(BEEPER_PIN, BEEPER_FREQUENCY, BEEPER_DURATION*5);
delay(BEEPER_DURATION*5+50);
}
}
void userBeep() {
tone(BEEPER_PIN, BEEPER_FREQUENCY, BEEPER_DURATION);
delay(BEEPER_DURATION*2);
tone(BEEPER_PIN, BEEPER_UFREQUENCY, BEEPER_UDURATION);
delay(BEEPER_UDURATION*2);
tone(BEEPER_PIN, BEEPER_UFREQUENCY, BEEPER_UDURATION);
}
bool setServoPos(int degree) {
return servo.setServoPos(degree);
}
void getEepromData() {
long pos;
EEPROM.get(EEPROM_SELECTOR_POS, pos);
steppers[SELECTOR].setStepPosition(pos);
EEPROM.get(EEPROM_REVOLVER_POS, pos);
steppers[REVOLVER].setStepPosition(pos);
EEPROM.get(EEPROM_FEEDER_POS, pos);
steppers[FEEDER].setStepPosition(pos);
EEPROM.get(EEPROM_TOOL, toolSelected);
EEPROM.get(EEPROM_CONTRAST, smuffConfig.lcdContrast);
EEPROM.get(EEPROM_TOOL_COUNT, smuffConfig.toolCount);
if (smuffConfig.toolCount < MIN_TOOLS || smuffConfig.toolCount > MAX_TOOLS) {
smuffConfig.toolCount = 5;
EEPROM.put(EEPROM_TOOL_COUNT, smuffConfig.toolCount);
}
}
void setSignalPort(int port, bool state) {
sprintf(tmp,"%c%c%s", 0x1b, port, state ? "1" : "0");
Serial2.write(tmp);
}
void signalSelectorReady() {
setSignalPort(SELECTOR_SIGNAL, false);
//__debug("Signalling Selector ready");
}
void signalSelectorBusy() {
setSignalPort(SELECTOR_SIGNAL, true);
//__debug("Signalling Selector busy");
}
void signalLoadFilament() {
setSignalPort(FEEDER_SIGNAL, true);
//__debug("Signalling load filament");
}
void signalUnloadFilament() {
setSignalPort(FEEDER_SIGNAL, false);
//__debug("Signalling unload filament");
}
void listDir(File root, int numTabs, int serial) {
while (true) {
File entry = root.openNextFile();
if (!entry)
break;
for (int i = 1; i < numTabs; i++) {
printResponse("\t", serial);
}
printResponse(entry.name(), serial);
if (entry.isDirectory()) {
printResponse("/\r\n", serial);
//listDir(entry, numTabs + 1, serial);
}
else {
sprintf(tmp, "\t\t%ld\r\n", entry.size());
printResponse(tmp, serial);
}
entry.close();
}
}
void __debug(const char* fmt, ...) {
#ifdef DEBUG
char _tmp[1024];
va_list arguments;
va_start(arguments, fmt);
vsnprintf(_tmp, 1024, fmt, arguments);
va_end (arguments);
Serial.println(_tmp);
#endif
}

309
src/SimpleGCodeParser.cpp Normal file
View File

@@ -0,0 +1,309 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Module implementing a simple G-Code parser
*/
#include "SMuFF.h"
#include "Config.h"
#include "ZTimerLib.h"
#include "ZStepperLib.h"
extern ZStepper steppers[NUM_STEPPERS];
char ptmp[80];
bool parserBusy = false;
unsigned int currentLine = 0;
void parseGcode(String serialBuffer, int serial) {
if(parserBusy) {
sendErrorResponseP(serial, P_Busy);
return;
}
serialBuffer.replace(" ","");
serialBuffer.replace("\r","");
serialBuffer.replace("\n","");
if(serialBuffer.length()==0)
return;
String line = String(serialBuffer);
parserBusy = true;
int pos;
if((pos = line.lastIndexOf("*")) > -1) {
line = line.substring(0, pos);
}
if((pos = line.lastIndexOf(";")) > -1) {
if(pos==0) {
parserBusy = false;
return;
}
line = line.substring(0, pos);
}
if(line.startsWith("N")) {
char ln[15];
if((int)(currentLine = getParam(line, (char*)"N")) != -1) {
sprintf(ln, "%d", currentLine);
line = line.substring(strlen(ln)+1);
}
}
//__debug("Line: %s %d", line.c_str(), line.length());
if(line.startsWith("G")) {
if(parse_G(line.substring(1), serial))
sendOkResponse(serial);
else
sendErrorResponseP(serial);
parserBusy = false;
return;
}
else if(line.startsWith("M")) {
if(parse_M(line.substring(1), serial))
sendOkResponse(serial);
else
sendErrorResponseP(serial);
parserBusy = false;
return;
}
else if(line.startsWith("T")) {
if(parse_T(line.substring(1), serial))
sendOkResponse(serial);
else
sendErrorResponseP(serial);
parserBusy = false;
return;
}
else {
char tmp[256];
sprintf(tmp, "%s '%s'\n", P_UnknownCmd, line.c_str());
//__debug("Err: %s", tmp);
sendErrorResponse(serial, tmp);
}
parserBusy = false;
}
bool parse_T(String buf, int serial) {
bool stat = true;
if(buf.length()==0) {
sendToolResponse(serial);
return stat;
}
int tool = buf.toInt();
int param;
char msg[10];
sprintf_P(msg, P_TResponse, tool);
int ofs = String(msg).length()-2;
if(tool == -1 || tool == 255) {
parse_G(String("28"), serial);
}
else if(tool >= 0 && tool <= smuffConfig.toolCount-1) {
stat = selectTool(tool, false);
if(stat) {
if((param = getParam(buf.substring(ofs), (char*)"S")) != -1) {
if(param == 1)
loadFilament(false);
else if(param == 0)
unloadFilament();
}
}
printResponse(msg, serial);
}
else {
sendErrorResponse(serial, "Wrong tool selected.");
stat = false;
}
return stat;
}
bool parse_G(String buf, int serial) {
bool stat = true;
if(buf.length()==0) {
sendGList(serial);
return stat;
}
int code = buf.toInt();
//__debug("G[%s]: >%d< %d", buf.c_str(), code, buf.length());
char msg[10];
sprintf_P(msg, P_GResponse, code);
int ofs = String(msg).length()-2;
for(int i=0; i< 999; i++) {
if(gCodeFuncsG[i].code == -1)
break;
if(gCodeFuncsG[i].code == code) {
return gCodeFuncsG[i].func(msg, buf.substring(ofs), serial);
}
}
char tmp[256];
sprintf_P(tmp, P_UnknownCmd, buf.c_str());
return false;
}
bool parse_M(String buf, int serial) {
bool stat = true;
if(buf.length()==0) {
sendMList(serial);
return stat;
}
int code = buf.toInt();
char msg[10];
sprintf_P(msg, P_MResponse, code);
int ofs = String(msg).length()-2;
for(int i=0; i< 999; i++) {
if(gCodeFuncsM[i].code == -1)
break;
if(gCodeFuncsM[i].code == code) {
//__debug("Calling: M", gCodeFuncsM[i].code);
return gCodeFuncsM[i].func(msg, buf.substring(ofs), serial);
}
}
char tmp[256];
sprintf_P(tmp, P_UnknownCmd, buf.c_str());
return false;
}
int getParam(String buf, char* token) {
int pos = buf.indexOf(token);
//__debug("getParam: %s\n",buf.c_str());
if(pos != -1) {
//__debug("getParam:pos: %d",pos);
if(buf.charAt(pos+1)=='-') {
int val = buf.substring(pos+2).toInt();
//__debug("Negative: %d", 0-val);
return 0-val;
}
return buf.substring(pos+1).toInt();
}
else
return -1;
}
bool getParamString(String buf, char* token, char* dest, int bufLen) {
int pos = buf.indexOf(token);
//__debug("getParamString: %s\n",buf.c_str());
if(pos != -1) {
if(buf.substring(pos+1).startsWith("\"")) {
int endPos = buf.substring(pos+2).indexOf("\"");
//__debug("End of string: %d\n", endPos);
if(endPos != -1) {
memset(dest, 0, bufLen);
if(endPos+1 < bufLen) {
if(dest != NULL) {
buf.substring(pos+2, endPos+3).toCharArray(dest, endPos+1);
//__debug("ptmp: >%s< (%d)\n", dest, strlen(dest));
}
return true;
}
}
}
}
return false;
}
void prepStepping(int index, long param, bool Millimeter /* = true */, bool ignoreEndstop /* = false */) {
if(param != 0) {
if(positionMode == RELATIVE) {
if(Millimeter) prepSteppingRelMillimeter(index, param);
else prepSteppingRel(index, param);
}
else {
if(Millimeter) prepSteppingAbsMillimeter(index, param);
else prepSteppingAbs(index, param);
}
remainingSteppersFlag |= _BV(index);
}
}
void sendGList(int serial) {
printResponseP(P_GCmds, serial);
}
void sendMList(int serial) {
printResponseP(P_MCmds, serial);
}
void sendToolResponse(int serial) {
char tmp[80];
sprintf_P(tmp, P_TResponse, toolSelected);
printResponse(tmp, serial);
}
void sendErrorResponse(int serial, const char* msg /* = NULL */) {
printResponse(msg, serial);
sendOkResponse(serial);
}
void sendErrorResponseP(int serial, const char* msg /* = NULL */) {
char tmp[128];
sprintf_P(tmp, P_Error, msg == NULL ? "" : msg);
printResponse(tmp, serial);
sendOkResponse(serial);
}
void sendOkResponse(int serial) {
printResponseP(P_Ok, serial);
}
void sendStartResponse(int serial){
printResponseP(P_Start, serial);
}
void saveSettings(int serial) {
printResponseP(P_AlreadySaved, serial);
}
void reportSettings(int serial) {
char tmp[128];
long ldummy;
byte bdummy;
EEPROM.get(EEPROM_SELECTOR_POS, ldummy); sprintf_P(tmp, P_SelectorPos, EEPROM_SELECTOR_POS, ldummy); printResponse(tmp, serial);
EEPROM.get(EEPROM_REVOLVER_POS, ldummy); sprintf_P(tmp, P_RevolverPos, EEPROM_REVOLVER_POS, ldummy); printResponse(tmp, serial);
EEPROM.get(EEPROM_FEEDER_POS, ldummy); sprintf_P(tmp, P_FeederPos, EEPROM_FEEDER_POS, ldummy); printResponse(tmp, serial);
EEPROM.get(EEPROM_TOOL, bdummy); sprintf_P(tmp, P_ToolSelected, EEPROM_TOOL, bdummy); printResponse(tmp, serial);
EEPROM.get(EEPROM_CONTRAST, bdummy); sprintf_P(tmp, P_Contrast, EEPROM_CONTRAST, bdummy); printResponse(tmp, serial);
EEPROM.get(EEPROM_TOOL_COUNT, bdummy); sprintf_P(tmp, P_ToolsConfig, EEPROM_TOOL_COUNT, bdummy); printResponse(tmp, serial);
}
void printResponse(const char* response, int serial) {
switch(serial) {
case 0: Serial.print(response); break;
case 1: Serial1.print(response); break;
case 2: Serial2.print(response); break;
case 3: Serial3.print(response); break;
}
}
void printResponseP(const char* response, int serial) {
switch(serial) {
case 0: Serial.print((__FlashStringHelper*)response); break;
case 1: Serial1.print((__FlashStringHelper*)response); break;
case 2: Serial2.print((__FlashStringHelper*)response); break;
case 3: Serial3.print((__FlashStringHelper*)response); break;
}
}

133
src/Strings.h Normal file
View File

@@ -0,0 +1,133 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _SMUFF_STRINGS_H
#define _SMUFF_STRINGS_H
#include <avr/pgmspace.h>
#define SD_ERR_INIT 1
#define SD_ERR_NOCONFIG 2
#define SD_READING_CONFIG 0
const char P_MenuItemBack [] PROGMEM = { "\u25c0 BACK\n" };
const char P_MenuItems [] PROGMEM = { "Home\nMotors off\nOffsets\nLoad feeder\nUnload feeder\nSwap tools" };
const char P_OfsMenuItems [] PROGMEM = { "Selector\nRevolver" };
const char P_OkButtonOnly [] PROGMEM = { " Ok " };
const char P_CancelButtonOnly [] PROGMEM = { " Cancel " };
const char P_OkCancelButtons [] PROGMEM = { " Ok \n Cancel " };
const char P_YesNoButtons [] PROGMEM = { " Yes \n No " };
const char P_TitleWarning [] PROGMEM = { "WARNING" };
const char P_TitleSelected [] PROGMEM = { "TOOL SELECTED" };
const char P_FeederLoaded [] PROGMEM = { "Feeder is loaded!\n" };
const char P_AskUnload [] PROGMEM = { "Want me to unload\nit now?" };
const char P_AskLoad [] PROGMEM = { "Want me to load\nit now?" };
const char P_SelectedTool [] PROGMEM = { "\n" };
const char P_CantLoad [] PROGMEM = { "Can't load feeder." };
const char P_CantUnload [] PROGMEM = { "Can't unload feeder." };
const char P_CheckUnit [] PROGMEM = { "Please check unit!" };
const char P_TitleConfigError [] PROGMEM = { "CONFIG FAILURE" };
const char P_ConfigFail1 [] PROGMEM = { "Your config file is" };
const char P_ConfigFail2 [] PROGMEM = { "possibly corrupted,\nplease check!" };
const char P_ConfigFail3 [] PROGMEM = { "too big,\nplease reduce content!" };
const char P_Tool [] PROGMEM = { "Tool " };
const char P_ToolMenu [] PROGMEM = { "Tool %d" };
const char P_SwapMenu [] PROGMEM = { "Slot %d: T%d" };
const char P_SwapReset [] PROGMEM = { "Reset swaps\n" };
const char P_SwapToolDialog [] PROGMEM = { "Swap Tool %d\nwith Tool %d" };
const char P_Selecting [] PROGMEM = { "Selecting" };
const char P_Wait [] PROGMEM = { "please wait..." };
const char P_TitleMainMenu [] PROGMEM = { "Main menu" };
const char P_TitleToolsMenu [] PROGMEM = { "Tool Selection" };
const char P_TitleOffsetsMenu [] PROGMEM = { "Offsets calibration" };
const char P_TitleSwapMenu [] PROGMEM = { "Swap tools" };
const char P_Busy[] PROGMEM = { "busy..." };
const char P_Ready[] PROGMEM = { "ready." };
const char P_SD_ReadingConfig[] PROGMEM = { "Reading config..." };
const char P_SD_InitError[] PROGMEM = { "SD-Card not ready!" };
const char P_SD_NoConfig[] PROGMEM = { "No config file found!" };
const char P_Ok[] PROGMEM = { "ok\n" };
const char P_Start[] PROGMEM = { "start\n" };
const char P_Error[] PROGMEM = { "Error: %s\n" };
const char P_UnknownCmd[] PROGMEM = { "Unknown command:" };
const char P_AlreadySaved[] PROGMEM = { "Already saved.\n" };
const char P_GVersion[] PROGMEM = { "FIRMWARE_NAME: Smart.Multi.Filament.Feeder (SMuFF) FIRMWARE_VERSION: %s ELECTRONICS: Wanhao i3-Mini DATE: %s\n" };
const char P_TResponse[] PROGMEM = { "T%d\n" };
const char P_GResponse[] PROGMEM = { "G%d\n" };
const char P_MResponse[] PROGMEM = { "M%d\n" };
const char P_M250Response[] PROGMEM = { "M250 C%d\n" };
const char P_SelectorPos[] PROGMEM = { "%3d: Selector position = %ld\n" };
const char P_RevolverPos[] PROGMEM = { "%3d: Revolver position = %ld\n" };
const char P_FeederPos[] PROGMEM = { "%3d: Feeder position = %ld\n" };
const char P_ToolSelected[] PROGMEM = { "%3d: Tool selected = %d\n" };
const char P_Contrast[] PROGMEM = { "%3d: Display contrast = %d\n" };
const char P_ToolsConfig[] PROGMEM = { "%3d: Tools configured = %d\n" };
const char P_AccelSpeed[] PROGMEM = { "X (Selector):\t%s\nY (Revolver):\t%s\nZ (Feeder):\t%s\n" };
const char P_CurrentTool[] PROGMEM = {"Tool " };
const char P_Feed[] PROGMEM = {"Feed " };
const char P_NoTool [] PROGMEM = { "No tool set.\n" };
const char P_Aborting [] PROGMEM = { "Aborting..."};
const char P_FeederJamed [] PROGMEM = { "Feeder is jamed.\n" };
const char P_ToolAlreadySet [] PROGMEM = { "Tool already set." };
const char P_WrongFormat [] PROGMEM = { "Wrong format. Use Bdd:dd:dd...\n" };
const char P_GCmds[] PROGMEM = {
"G0\t-\tMove\n" \
"G1\t-\tMove\n" \
"G4\t-\tDwell\n" \
"G12\t-\tWipe filament\n" \
"G28\t-\tHome\n" \
"G90\t-\tAbsolute positioning\n" \
"G91\t-\tRelative positioning\n" };
const char P_MCmds[] PROGMEM = {
"M18\t-\tMotors off\n" \
"M84\t-\tMotors off\n" \
"M20\t-\tList SD-Card\n" \
"M42\t-\tSet pin state\n" \
"M106\t-\tFan on\n" \
"M107\t-\tFan off\n" \
"M114\t-\tReport current positions\n" \
"M115\t-\tReport version\n" \
"M117\t-\tDisplay message\n" \
"M119\t-\tReport endstop status\n" \
"M120\t-\tEnable endstops\n" \
"M121\t-\tDisable endstops\n" \
"M122\t-\tReport Diagnostics\n" \
"M201\t-\tSet max acceleration\n" \
"M203\t-\tSet max feedrate\n" \
"M206\t-\tSet offsets\n" \
"M250\t-\tLCD contrast\n" \
"M300\t-\tBeep\n" \
"M500\t-\tSave settings\n" \
"M503\t-\tReport settings\n" \
"M700\t-\tLoad filament\n" \
"M701\t-\tUnload filament\n" \
"M999\t-\tReset\n" \
"M2000\t-\tText to decimal\n" \
"M2001\t-\tDecimal to text\n"};
#endif

55
src/ZServo.cpp Normal file
View File

@@ -0,0 +1,55 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ZServo.h"
ZServo* instance;
void ZServo::write(int degree) {
setServoPos(degree);
}
bool ZServo::setServoPos(int degree) {
bool stat = false;
if(_pin > 0) {
_pulseLen = map(degree, 0, 180, US_PER_PULSE_0DEG, US_PER_PULSE_180DEG);
instance = this;
//__debug("Pulse length set to: %dms", _pulseLen);
servoTimer.setNextInterruptInterval(TIMER_INTERVAL);
stat = true;
_degree = degree;
}
return stat;
}
void ZServo::setServoMS(int microseconds) {
_pulseLen = microseconds;
servoTimer.setNextInterruptInterval(TIMER_INTERVAL);
}
void ZServo::setServo() {
digitalWrite(_pin, HIGH);
delayMicroseconds(_pulseLen);
digitalWrite(_pin, LOW);
}
void isrServoTimerHandler() {
if(instance != NULL)
instance->setServo();
}

61
src/ZServo.h Normal file
View File

@@ -0,0 +1,61 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <Arduino.h>
#include "Config.h"
#include "ZTimerLib.h"
#ifndef _ZSERVO_H
#define _ZSERVO_H
#define US_PER_PULSE_0DEG 500 // 0 degrees
#define US_PER_PULSE_180DEG 2400 // 180 degrees
#define TIMER_INTERVAL 312 // CPU-Clock / (Prescaler * 50)-1 = 16000000 / (1024 * 50) - 1 = 311,5
void isrServoTimerHandler();
class ZServo {
public:
ZServo() { _pin = 0; };
ZServo(int pin) { attach(pin); }
void attach(int pin) {
_pin = pin;
pinMode(_pin, OUTPUT);
digitalWrite(_pin, 0);
servoTimer.setupTimer(ZTimer::TIMER5, ZTimer::PRESCALER1024);
servoTimer.setupTimerHook(isrServoTimerHandler);
}
void write(int degree);
bool setServoPos(int degree);
void setServoMS(int microseconds);
void setServo();
void stop() { servoTimer.stopTimer(); }
int getDegree() { return _degree; }
private:
ZTimer servoTimer;
int _pin;
int _degree;
int _pulseLen;
};
#endif

183
src/ZStepperLib.cpp Normal file
View File

@@ -0,0 +1,183 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Module implementing a stepper driver library
*/
#include "ZStepperLib.h"
extern void __debug(const char* fmt, ...);
ZStepper::ZStepper() {
}
ZStepper::ZStepper(int number, char* descriptor, int stepPin, int dirPin, int enablePin, float acceleration, unsigned int minStepInterval) {
_number = number;
_descriptor = descriptor;
_stepPin = stepPin;
_dirPin = dirPin;
_enablePin = enablePin;
_acceleration = acceleration;
_minStepInterval = minStepInterval;
pinMode(_stepPin, OUTPUT);
pinMode(_dirPin, OUTPUT);
pinMode(_enablePin, OUTPUT);
}
void ZStepper::defaultStepFunc(void) {
if(_stepPin != -1) {
digitalWrite(_stepPin, HIGH);
digitalWrite(_stepPin, LOW);
}
}
void ZStepper::resetStepper() {
_duration = _acceleration;
_durationInt = _duration;
_stepCount = 0;
_movementDone = false;
_endstopHit = false;
}
void ZStepper::prepareMovement(long steps, boolean ignoreEndstop = false) {
setDirection(steps < 0 ? CCW : CW);
_totalSteps = abs(steps);
_accelDistance = _totalSteps >> 5;
_stepsAcceleration = (float)((_acceleration - _minStepInterval)+.1) / _accelDistance;
_ignoreEndstop = ignoreEndstop;
resetStepper();
}
void ZStepper::setEndstop(int pin, int triggerState, EndstopType type) {
_endstopPin = pin;
_endstopState = triggerState;
_endstopType = type;
pinMode(_endstopPin, _endstopType == LOW ? INPUT_PULLUP : INPUT);
_endstopHit = digitalRead(_endstopPin) == _endstopState;
}
void ZStepper::setDirection(ZStepper::MoveDirection direction) {
if(_dirPin != -1) {
_dir = direction;
digitalWrite(_dirPin, !_invertDir ? (_dir == CCW ? HIGH : LOW) : (_dir == CCW ? LOW : HIGH));
}
}
void ZStepper::setEnabled(boolean state) {
if(_enablePin != -1) {
digitalWrite(_enablePin, state ? LOW : HIGH);
_enabled = state;
}
}
void ZStepper::updateAcceleration() {
if(_stepCount <= _accelDistance) {
_duration -= _stepsAcceleration; // accelerate
if(_duration <= _minStepInterval)
_duration = _minStepInterval;
//__debug("durInt: %d, %s", _durationInt, String(_duration).c_str());
}
else if (_stepCount >= _totalSteps - _accelDistance ) {
_duration += _stepsAcceleration; // decelerate
if(_duration >= _acceleration)
_duration = _acceleration;
}
_durationInt = _duration;
}
void ZStepper::handleISR() {
//if(_endstopType == ORBITAL)
// __debug("O: %d %d ", _stepCount, _dir);
if((_endstopType == MIN && _dir == CCW) ||
(_endstopType == MAX && _dir == CW) ||
(_endstopType == ORBITAL && _dir == CCW)) {
bool hit = digitalRead(_endstopPin)==_endstopState;
setEndstopHit(hit);
}
if(!_ignoreEndstop && _endstopHit && !_movementDone){
setMovementDone(true);
if(_endstopType == MIN || _endstopType == ORBITAL) {
setStepPosition(0);
}
else if(_endstopType == MAX)
setStepPosition(_maxStepCount);
if(endstopFunc != NULL)
endstopFunc();
return;
}
if(_ignoreEndstop && _endstopHit && _endstopType == ORBITAL){
setStepPosition(0);
}
if(_maxStepCount != 0 && _dir == CW && _stepCount >= _maxStepCount) {
setMovementDone(true);
}
else if(_stepCount < _totalSteps) {
if(stepFunc != NULL)
stepFunc();
else
defaultStepFunc();
_stepCount++;
setStepPosition(_stepPosition + _dir);
if(_stepCount >= _totalSteps) {
setMovementDone(true);
//__debug("handleISR(): %ld / %ld", _stepCount, _totalSteps);
}
}
updateAcceleration();
}
bool ZStepper::getEndstopHit() {
int stat = 0;
for(int i=0; i < 5; i++)
stat = digitalRead(_endstopPin);
setEndstopHit(stat==_endstopState);
return _endstopHit;
}
void ZStepper::home() {
unsigned int curSpeed = getMaxSpeed();
long distance = -_maxStepCount;
if(_endstopPin != -1) {
distance = -(_maxStepCount*2);
}
//__debug("[ZStepper::home] Distance: %d - max: %d", distance, _maxStepCount);
prepareMovement(distance);
//__debug("[ZStepper::home] DONE prepareMovement");
if(runAndWaitFunc != NULL)
runAndWaitFunc(_number);
//__debug("[ZStepper::home] DONE runAndWait");
do {
prepareMovement(_maxStepCount/30);
if(runAndWaitFunc != NULL)
runAndWaitFunc(_number);
} while(_endstopHit);
prepareMovement(-(_maxStepCount));
setMaxSpeed(curSpeed*5);
if(runAndWaitFunc != NULL)
runAndWaitFunc(_number);
setMaxSpeed(curSpeed);
}

135
src/ZStepperLib.h Normal file
View File

@@ -0,0 +1,135 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <Arduino.h>
#include "Config.h"
#ifndef _ZSTEPPER_H
#define _ZSTEPPER_H
extern void __debug(const char* fmt, ...);
class ZStepper {
public:
typedef enum {
NONE = -1,
MIN, // Endstop is at position 0
MAX, // Endstop is at position >0
ORBITAL // Endstop on rotating axis
} EndstopType;
typedef enum {
CW = 1, // Clockwise
CCW = -1 // Counter clockwise
} MoveDirection;
ZStepper();
ZStepper(int number, char* descriptor, int stepPin, int dirPin, int enablePin, float accelaration, unsigned int minStepInterval);
void prepareMovement(long steps, boolean ignoreEndstop = false);
void handleISR();
void home();
void (*stepFunc)() = NULL;
void (*endstopFunc)() = NULL;
void (*runAndWaitFunc)(int number) = NULL;
void (*runNoWaitFunc)(int number) = NULL;
void defaultStepFunc(); // default step method, uses digitalWrite on _stepPin
char* getDescriptor() { return _descriptor; }
void setDescriptor(char* descriptor) { _descriptor = descriptor; }
MoveDirection getDirection() { return _dir; }
void setDirection(MoveDirection newDir);
bool getEnabled() { return _enabled; }
void setEnabled(bool state);
void setEndstop(int pin, int triggerState, EndstopType type);
EndstopType getEndstopType() { return _endstopType; }
void setEndstopType(EndstopType type) { _endstopType = type; }
int getEndstopState() { return _endstopState; }
void setEndstopState(int state) { _endstopState = state; }
bool getEndstopHit();
bool getEndstopHitAlt() { return _endstopHit; }
void setEndstopHit(int state) { _endstopHit = state; }
int getEndstopPin() { return _endstopPin; }
void setEndstopPin(int pin) { _endstopPin = pin; }
bool getIgnoreEndstop() { return _ignoreEndstop; }
void setIgnoreEndstop(bool state) { _ignoreEndstop = state; }
long getStepCount() { return _stepCount; }
void setStepCount(long count) { _stepCount = count; }
long getMaxStepCount() { return _maxStepCount; }
void setMaxStepCount(long count) { _maxStepCount = count; }
void incrementStepCount() { _stepCount++; }
long getTotalSteps() { return _totalSteps; }
void setTotalSteps(long count) { _totalSteps = count; }
long getStepPosition() { return _stepPosition; }
void setStepPosition(long position) { _stepPosition = position; _stepPositionMM = (float)((float)position / _stepsPerMM); }
float getStepPositionMM() { return _stepPositionMM; }
void setStepPositionMM(float position) { _stepPositionMM = position; _stepPosition = (long)(position * _stepsPerMM);}
void incrementStepPosition() { setStepPosition(getStepPosition() + _dir); }
bool getMovementDone() { return _movementDone; }
void setMovementDone(bool state) { _movementDone = state; }
float getAcceleration() { return _acceleration; }
void setAcceleration(float value) { _acceleration = value; }
unsigned int getMaxSpeed() { return _minStepInterval; }
void setMaxSpeed(unsigned int value) { _minStepInterval = value; }
bool getInvertDir() { return _invertDir; }
void setInvertDir(bool state) { _invertDir = state; }
unsigned int getDuration() { return _durationInt; }
void setDuration(unsigned int value) { _durationInt = value; }
unsigned int getStepsPerMM() { return _stepsPerMM; }
void setStepsPerMM(int steps) { _stepsPerMM = steps; }
private:
int _number = 0; // index of this stepper
char* _descriptor = (char*)""; // display name for this stepper
int _stepPin = -1; // stepping pin
int _dirPin = -1; // direction pin
int _enablePin = -1; // enable pin
bool _enabled = false; // enabled state
int _endstopPin = -1; // endstop pin
volatile bool _endstopHit = false; // set when endstop is being triggered
bool _ignoreEndstop = false; // flag whether or not to ignore endstop trigger
int _endstopState = HIGH; // value for endstop triggered
EndstopType _endstopType = NONE; // type of endstop (MIN, MAX, ORBITAL etc)
volatile long _stepPosition = 0; // current position of stepper (total of all movements taken so far)
volatile MoveDirection _dir = CW; // current direction of movement, used to keep track of position
volatile long _totalSteps = 0; // number of steps requested for current movement
volatile bool _movementDone = false; // true if the current movement has been completed (used by main program to wait for completion)
float _acceleration = 1000; // acceleration value
unsigned int _minStepInterval = 100; // ie. max speed, smaller is faster
long _stepCount = 0; // number of steps completed in current movement
long _maxStepCount = 0; // maximum number of steps
unsigned int _stepsPerMM = 0; // steps needed for one millimeter
float _stepPositionMM = 0; // current position of stepper in millimeter
bool _invertDir = false; // stepper direction inversion
// per iteration variables (potentially changed every interrupt)
volatile float _duration; // current interval length
volatile unsigned int _durationInt; // above variable truncated
volatile long _accelDistance = 0; // amount of steps for acceleration/deceleration
volatile float _stepsAcceleration = 0.0;
void resetStepper(); // method to reset work params
void updateAcceleration();
};
#endif

148
src/ZTimerLib.cpp Normal file
View File

@@ -0,0 +1,148 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Module for timer & ISR routines
*/
#include "ZTimerLib.h"
static void (*__timer1Hook)(void) = NULL;
static void (*__timer3Hook)(void) = NULL;
static void (*__timer4Hook)(void) = NULL;
static void (*__timer5Hook)(void) = NULL;
void ZTimer::setupTimer(IsrTimer timer, TimerPrescaler prescaler) {
_timer = timer;
stopTimer();
noInterrupts();
switch(_timer) {
case TIMER1:
TCCR1A = 0;
TCCR1B = prescaler;
TCCR1B |= _BV(WGM12); // CTC mode
break;
case TIMER3:
TCCR3A = 0;
TCCR3B = prescaler;
TCCR3B |= _BV(WGM32); // CTC mode
break;
case TIMER4:
TCCR4A = 0;
TCCR4B = prescaler;
TCCR4B |= _BV(WGM42); // CTC mode
break;
case TIMER5:
TCCR5A = 0;
TCCR5B = prescaler;
TCCR5B |= _BV(WGM52); // CTC mode
break;
}
setNextInterruptInterval(1000);
interrupts();
}
ISR(TIMER1_COMPA_vect) {
if(__timer1Hook != NULL)
__timer1Hook();
}
ISR(TIMER3_COMPA_vect) {
if(__timer3Hook != NULL)
__timer3Hook();
}
ISR(TIMER4_COMPA_vect) {
if(__timer4Hook != NULL)
__timer4Hook();
}
ISR(TIMER5_COMPA_vect) {
if(__timer5Hook != NULL)
__timer5Hook();
}
void ZTimer::setupTimerHook(void (*function)(void))
{
switch(_timer) {
case TIMER1: __timer1Hook = function; break;
case TIMER3: __timer3Hook = function; break;
case TIMER4: __timer4Hook = function; break;
case TIMER5: __timer5Hook = function; break;
}
}
void ZTimer::setNextInterruptInterval(unsigned int interval) {
stopTimer();
switch(_timer) {
case TIMER1: OCR1A = interval; TCNT1 = 0; break;
case TIMER3: OCR3A = interval; TCNT3 = 0; break;
case TIMER4: OCR4A = interval; TCNT4 = 0; break;
case TIMER5: OCR5A = interval; TCNT5 = 0; break;
}
startTimer();
}
unsigned int ZTimer::getOCRxA() {
switch(_timer) {
case TIMER1: return OCR1A;
case TIMER3: return OCR3A;
case TIMER4: return OCR4A;
case TIMER5: return OCR5A;
}
return 0;
}
void ZTimer::setOCRxA(unsigned int value) {
switch(_timer) {
case TIMER1: OCR1A = value; break;
case TIMER3: OCR3A = value; break;
case TIMER4: OCR4A = value; break;
case TIMER5: OCR5A = value; break;
}
}
void ZTimer::setTCNTx(unsigned int value) {
switch(_timer) {
case TIMER1: TCNT1 = value; break;
case TIMER3: TCNT3 = value; break;
case TIMER4: TCNT4 = value; break;
case TIMER5: TCNT5 = value; break;
}
}
void ZTimer::startTimer() {
switch(_timer) {
case TIMER1: TIMSK1 |= _BV(OCIE1A); break;
case TIMER3: TIMSK3 |= _BV(OCIE3A); break;
case TIMER4: TIMSK4 |= _BV(OCIE4A); break;
case TIMER5: TIMSK5 |= _BV(OCIE5A); break;
}
}
void ZTimer::stopTimer() {
switch(_timer) {
case TIMER1: TIMSK1 &= ~_BV(OCIE1A); break;
case TIMER3: TIMSK3 &= ~_BV(OCIE3A); break;
case TIMER4: TIMSK4 &= ~_BV(OCIE4A); break;
case TIMER5: TIMSK5 &= ~_BV(OCIE5A); break;
}
}

61
src/ZTimerLib.h Normal file
View File

@@ -0,0 +1,61 @@
/**
* SMuFF Firmware
* Copyright (C) 2019 Technik Gegg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <Arduino.h>
#include "Config.h"
#ifndef _ZTIMER_H
#define _ZTIMER_H
extern void __debug(const char* fmt, ...);
class ZTimer {
public:
typedef enum {
TIMER1 = 1,
TIMER3 = 3,
TIMER4 = 4,
TIMER5 = 5
} IsrTimer;
typedef enum {
PRESCALER1 = 1,
PRESCALER8 = 2,
PRESCALER64 = 3,
PRESCALER256 = 4,
PRESCALER1024 = 5
} TimerPrescaler;
ZTimer() { };
void setupTimer(IsrTimer timer, TimerPrescaler prescaler);
void setupTimerHook(void (*function)(void));
void setNextInterruptInterval(unsigned int interval);
unsigned int getOCRxA();
void setOCRxA(unsigned int value);
void setTCNTx(unsigned int value);
void startTimer();
void stopTimer();
private:
IsrTimer _timer;
};
#endif

11
test/README Normal file
View File

@@ -0,0 +1,11 @@
This directory is intended for PIO Unit Testing 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 PIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html