diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ac4a2b --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +MidiStepper +=========== + +Made for an old (AnyCubic) Trigorilla-Board from an 3D-Printer. +Also compatible with Arduino Nano and old floppy drives. + +Floppy pins: +------------ + + .....j..DS....... + ..G5 . ...j..GG....... + + j: jumper (drive select) + D: direction + S: step + G: corresponding ground + 5: 5V for the motors + +Caveats: +-------- +The ATmega2560 has 6 timers, so we can use them to generate the square waves for the steps. Be aware that delay() and similar arduino-functions will not work as expected if you extend this and use Timer0. RAMPS14-compatible boards have only 5 stepper drivers, so we're fine. +Other arduinos don't have enough timer peripherals, so we generate them in software. This could be done in an ISR. See: https://www.youtube.com/watch?v=fHAO7SW-SZI + +Usage: +------ + +The arduino part accepts raw midi messages over its serial interface. baudrate is 115200. +Use the python script send.py to send midi files over the serial port. It can also act as a virtual midi port. + +Compile: +-------- + $ git clone + $ virtualenv -p python --no-site-packages . + $ source bin/activate + $ pip install -U platformio + $ pio run + $ pio run -t upload \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 7c87531..bb52667 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,10 +10,12 @@ [platformio] default_envs = megaatmega2560 +#default_envs = nano [common] lib_deps = ToneLibrary +# TimerOne # AccelStepper # SpeedyStepper MIDI Library @@ -30,3 +32,9 @@ platform = atmelavr board = megaatmega2560 board_build.f_cpu = 16000000L lib_deps = ${common.lib_deps} + +[env:nano] +platform = atmelavr +board = nanoatmega328 +board_build.f_cpu = 16000000L +lib_deps = ${common.lib_deps} diff --git a/send.py b/send.py index bc8c10b..c326a9c 100644 --- a/send.py +++ b/send.py @@ -4,7 +4,7 @@ import sys, argparse import time import threading -from mido import MidiFile +import mido import serial drum_channels = [9] @@ -23,16 +23,22 @@ def main(argv): inputfile = '' outputport = '' parser = argparse.ArgumentParser() - parser.add_argument('midifilename', help="the midi file you want to send") + parser.add_argument('midifilename', help="the midi file you want to send. Set 'ALSA' for virtual midi port, see 'aconnect -o'") parser.add_argument('-P', '--serialport', help="serial port to open", default="/dev/ttyUSB0") parser.add_argument('--with-drums', action="store_true", help="send percussion track 10") + parser.add_argument('--delay', help="add delay before startup", type=int, default=1) args = parser.parse_args() with serial.Serial(args.serialport, 115200, timeout=12) as ser: receivethread = threading.Thread(target=serialreceive, args=(ser,)) receivethread.daemon = True receivethread.start() - time.sleep(1) - with MidiFile(args.midifilename) as mid: + time.sleep(args.delay) + if args.midifilename == 'ALSA': +# portmidi = mido.Backend('mido.backends.portmidi') + midi_in = mido.open_input() + else: + midi_in = mido.MidiFile(args.midifilename) + with midi_in as mid: for msg in mid: time.sleep(msg.time) if not msg.is_meta: diff --git a/src/main.cpp b/src/main.cpp index 3896ad3..ff20316 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,9 +5,26 @@ */ #include +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) #include "pins/pins_TRIGORILLA_14.h" - -#include +#elif defined(__AVR_ATmega328P__) +#define LED_PIN LED_BUILTIN +#define X_DIR_PIN PD2 +#define X_STEP_PIN PD3 +#define X_ENABLE_PIN A0 +#define Y_DIR_PIN PD4 +#define Y_STEP_PIN PD5 +#define Y_ENABLE_PIN A1 +#define Z_DIR_PIN PD6 +#define Z_STEP_PIN PD7 +#define Z_ENABLE_PIN A2 +#define E0_DIR_PIN PB0 +#define E0_STEP_PIN PB1 +#define E0_ENABLE_PIN A3 +#define E1_DIR_PIN PB2 +#define E1_STEP_PIN PB3 +#define E1_ENABLE_PIN A4 +#endif #include //#include "midi/noteList.h" @@ -24,7 +41,11 @@ MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial, MIDI, MySettings); const int NUM_STEPPER = 5; +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) TimerStepper stepper[NUM_STEPPER]; +#elif defined(__AVR_ATmega328P__) +LoopStepper stepper[NUM_STEPPER]; +#endif unsigned long previousMillis[NUM_STEPPER] = {0}; // will store last time LED was updated const long interval = 1000; // interval at which to blink (milliseconds) @@ -140,6 +161,19 @@ void setup() stepper[3].begin(E0_DIR_PIN, E0_STEP_PIN, E0_ENABLE_PIN); stepper[4].begin(E1_DIR_PIN, E1_STEP_PIN, E1_ENABLE_PIN); + + for (int i = 0; i < NUM_STEPPER; i++) { +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + stepper[i].setMaxFrequency(12000); +#elif defined(__AVR_ATmega328P__) + stepper[i].setMaxFrequency(1400); + Serial.print("homing stepper "); + Serial.println(i); + stepper[i].setMaxPosition(80); +#endif + } + + MIDI.setHandleNoteOn(handleNoteOn); MIDI.setHandleNoteOff(handleNoteOff); MIDI.setHandlePitchBend(handlePitchBend); diff --git a/src/stepper.h b/src/stepper.h index 69b96a7..98be6bf 100644 --- a/src/stepper.h +++ b/src/stepper.h @@ -3,6 +3,7 @@ #include #include +//#include class FrequencyLUT { public: @@ -12,7 +13,11 @@ class FrequencyLUT { }; FrequencyLUT::FrequencyLUT() { +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) int pitch = 24; +#elif defined(__AVR_ATmega328P__) + int pitch = 0; +#endif frequencies = malloc(128*sizeof(uint16_t)); for (int d=0; d<128; d++) { frequencies[d] = pow(2.0, (d-69.0+pitch)/12.0) * 440.0; @@ -35,12 +40,14 @@ class Stepper { virtual void stopTone() = 0; void enable(); void disable(); + void setMaxFrequency(uint16_t max_freq); protected: int8_t currentNote; int dir_pin; int step_pin; int enable_pin; uint8_t multiplicator = 4; + uint16_t maxFrequency; }; class TimerStepper : public Stepper { @@ -59,8 +66,12 @@ class LoopStepper : public Stepper { void playTone(uint16_t freq) override; void stopTone() override; void loop() override; + void setMaxPosition(int pos); private: - unsigned long currentStepMicros; + unsigned long currentStepMicros = 0; + unsigned long currentPosition; + unsigned long maxPosition; + bool direction; unsigned long prevStepMicros; }; @@ -81,6 +92,7 @@ void Stepper::enable() { } void Stepper::disable() { + stopTone(); digitalWrite(enable_pin, HIGH); } @@ -88,7 +100,7 @@ void Stepper::playNote(int8_t midi_note) { currentNote = midi_note; uint16_t f = frequencies[midi_note]; f *= multiplicator; - while (f > 12000) f/=2; + while (f > maxFrequency) f/=2; playTone(f); } @@ -98,6 +110,10 @@ void Stepper::bend(int value) { playTone(f_b); } +void Stepper::setMaxFrequency(uint16_t max_freq) { + maxFrequency = max_freq; +} + void TimerStepper::begin(int dir_pin, int step_pin, int enable_pin) { Stepper::begin(dir_pin, step_pin, enable_pin); tone.begin(step_pin); @@ -128,15 +144,34 @@ void LoopStepper::stopTone() { currentStepMicros = 0; } +void LoopStepper::setMaxPosition(int pos) { + maxPosition = 2*pos; + currentPosition = 2*pos; + currentStepMicros = 4545UL; + direction = 1; + prevStepMicros = micros(); + while (currentPosition > 0) loop(); + while (currentPosition != 2) loop(); + currentPosition = 0; maxPosition -=4; + direction = 0; + currentStepMicros = 0; +} + void LoopStepper::loop() { if (currentStepMicros == 0) return; + if (micros() - prevStepMicros >= currentStepMicros) { prevStepMicros += currentStepMicros; - digitalWrite(step_pin, HIGH); - digitalWrite(step_pin, LOW); + digitalWrite(step_pin, !digitalRead(step_pin)); +// digitalWrite(step_pin, HIGH); +// digitalWrite(step_pin, LOW); + if (direction == 0) currentPosition++; + else currentPosition--; + if (currentPosition >= maxPosition) direction = 1; + if (currentPosition <= 0) direction = 0; + digitalWrite(dir_pin, direction); } } - #endif /* _STEPPER_H */