Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:31e91bb0ef3c, committed 2012-07-31
- Comitter:
- scachat
- Date:
- Tue Jul 31 21:11:18 2012 +0000
- Commit message:
- smoothie port to mbed online compiler
Changed in this revision
diff -r 000000000000 -r 31e91bb0ef3c libs/ADC/adc.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/ADC/adc.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,439 @@
+/* mbed Library - ADC
+ * Copyright (c) 2010, sblandford
+ * released under MIT license http://mbed.org/licence/mit
+ */
+#include "mbed.h"
+#include "adc.h"
+
+
+ADC *ADC::instance;
+
+ADC::ADC(int sample_rate, int cclk_div)
+ {
+
+ int i, adc_clk_freq, pclk, clock_div, max_div=1;
+
+ //Work out CCLK
+ adc_clk_freq=CLKS_PER_SAMPLE*sample_rate;
+ int m = (LPC_SC->PLL0CFG & 0xFFFF) + 1;
+ int n = (LPC_SC->PLL0CFG >> 16) + 1;
+ int cclkdiv = LPC_SC->CCLKCFG + 1;
+ int Fcco = (2 * m * XTAL_FREQ) / n;
+ int cclk = Fcco / cclkdiv;
+
+ //Power up the ADC
+ LPC_SC->PCONP |= (1 << 12);
+ //Set clock at cclk / 1.
+ LPC_SC->PCLKSEL0 &= ~(0x3 << 24);
+ switch (cclk_div) {
+ case 1:
+ LPC_SC->PCLKSEL0 |= 0x1 << 24;
+ break;
+ case 2:
+ LPC_SC->PCLKSEL0 |= 0x2 << 24;
+ break;
+ case 4:
+ LPC_SC->PCLKSEL0 |= 0x0 << 24;
+ break;
+ case 8:
+ LPC_SC->PCLKSEL0 |= 0x3 << 24;
+ break;
+ default:
+ printf("Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n",
+ cclk_div);
+ printf("Defaulting to 1.\n");
+ LPC_SC->PCLKSEL0 |= 0x1 << 24;
+ break;
+ }
+ pclk = cclk / cclk_div;
+ clock_div=pclk / adc_clk_freq;
+
+ if (clock_div > 0xFF) {
+ //printf("Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n",
+ // clock_div);
+ clock_div=0xFF;
+ }
+ if (clock_div == 0) {
+ printf("Warning: Clock division is 0. Re-Setting to 1.\n");
+ clock_div=1;
+ }
+
+ _adc_clk_freq=pclk / clock_div;
+ if (_adc_clk_freq > MAX_ADC_CLOCK) {
+ printf("Warning: Actual ADC sample rate of %u which is above %u limit\n",
+ _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE);
+ while ((pclk / max_div) > MAX_ADC_CLOCK) max_div++;
+ printf("Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE);
+ }
+
+ LPC_ADC->ADCR =
+ ((clock_div - 1 ) << 8 ) | //Clkdiv
+ ( 1 << 21 ); //A/D operational
+
+ //Default no channels enabled
+ LPC_ADC->ADCR &= ~0xFF;
+ //Default NULL global custom isr
+ _adc_g_isr = NULL;
+ //Initialize arrays
+ for (i=7; i>=0; i--) {
+ _adc_data[i] = 0;
+ _adc_isr[i] = NULL;
+ }
+
+
+ //* Attach IRQ
+ instance = this;
+ NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr);
+
+ //Disable global interrupt
+ LPC_ADC->ADINTEN &= ~0x100;
+
+};
+
+void ADC::_adcisr(void)
+{
+ instance->adcisr();
+}
+
+
+void ADC::adcisr(void)
+{
+ uint32_t stat;
+ int chan;
+
+ // Read status
+ stat = LPC_ADC->ADSTAT;
+ //Scan channels for over-run or done and update array
+ if (stat & 0x0101) _adc_data[0] = LPC_ADC->ADDR0;
+ if (stat & 0x0202) _adc_data[1] = LPC_ADC->ADDR1;
+ if (stat & 0x0404) _adc_data[2] = LPC_ADC->ADDR2;
+ if (stat & 0x0808) _adc_data[3] = LPC_ADC->ADDR3;
+ if (stat & 0x1010) _adc_data[4] = LPC_ADC->ADDR4;
+ if (stat & 0x2020) _adc_data[5] = LPC_ADC->ADDR5;
+ if (stat & 0x4040) _adc_data[6] = LPC_ADC->ADDR6;
+ if (stat & 0x8080) _adc_data[7] = LPC_ADC->ADDR7;
+
+ // Channel that triggered interrupt
+ chan = (LPC_ADC->ADGDR >> 24) & 0x07;
+ //User defined interrupt handlers
+ if (_adc_isr[chan] != NULL)
+ _adc_isr[chan](_adc_data[chan]);
+ if (_adc_g_isr != NULL)
+ _adc_g_isr(chan, _adc_data[chan]);
+ return;
+}
+
+int ADC::_pin_to_channel(PinName pin) {
+ int chan;
+ switch (pin) {
+ case p15://=p0.23 of LPC1768
+ default:
+ chan=0;
+ break;
+ case p16://=p0.24 of LPC1768
+ chan=1;
+ break;
+ case p17://=p0.25 of LPC1768
+ chan=2;
+ break;
+ case p18://=p0.26 of LPC1768
+ chan=3;
+ break;
+ case p19://=p1.30 of LPC1768
+ chan=4;
+ break;
+ case p20://=p1.31 of LPC1768
+ chan=5;
+ break;
+ }
+ return(chan);
+}
+
+PinName ADC::channel_to_pin(int chan) {
+ const PinName pin[8]={p15, p16, p17, p18, p19, p20, p15, p15};
+
+ if ((chan < 0) || (chan > 5))
+ fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan);
+ return(pin[chan & 0x07]);
+}
+
+
+int ADC::channel_to_pin_number(int chan) {
+ const int pin[8]={15, 16, 17, 18, 19, 20, 0, 0};
+
+ if ((chan < 0) || (chan > 5))
+ fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan);
+ return(pin[chan & 0x07]);
+}
+
+
+uint32_t ADC::_data_of_pin(PinName pin) {
+ //If in burst mode and at least one interrupt enabled then
+ //take all values from _adc_data
+ if (burst() && (LPC_ADC->ADINTEN & 0x3F)) {
+ return(_adc_data[_pin_to_channel(pin)]);
+ } else {
+ //Return current register value or last value from interrupt
+ switch (pin) {
+ case p15://=p0.23 of LPC1768
+ default:
+ return(LPC_ADC->ADINTEN & 0x01?_adc_data[0]:LPC_ADC->ADDR0);
+ case p16://=p0.24 of LPC1768
+ return(LPC_ADC->ADINTEN & 0x02?_adc_data[1]:LPC_ADC->ADDR1);
+ case p17://=p0.25 of LPC1768
+ return(LPC_ADC->ADINTEN & 0x04?_adc_data[2]:LPC_ADC->ADDR2);
+ case p18://=p0.26 of LPC1768:
+ return(LPC_ADC->ADINTEN & 0x08?_adc_data[3]:LPC_ADC->ADDR3);
+ case p19://=p1.30 of LPC1768
+ return(LPC_ADC->ADINTEN & 0x10?_adc_data[4]:LPC_ADC->ADDR4);
+ case p20://=p1.31 of LPC1768
+ return(LPC_ADC->ADINTEN & 0x20?_adc_data[5]:LPC_ADC->ADDR5);
+ }
+ }
+}
+
+//Enable or disable an ADC pin
+void ADC::setup(PinName pin, int state) {
+ int chan;
+ chan=_pin_to_channel(pin);
+ if ((state & 1) == 1) {
+ switch(pin) {
+ case p15://=p0.23 of LPC1768
+ default:
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+ LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14;
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+ LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14;
+ break;
+ case p16://=p0.24 of LPC1768
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+ LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16;
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+ LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16;
+ break;
+ case p17://=p0.25 of LPC1768
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+ LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18;
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+ LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18;
+ break;
+ case p18://=p0.26 of LPC1768:
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+ LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20;
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+ LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20;
+ break;
+ case p19://=p1.30 of LPC1768
+ LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+ LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28;
+ LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+ LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28;
+ break;
+ case p20://=p1.31 of LPC1768
+ LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+ LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30;
+ LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+ LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30;
+ break;
+ }
+ //Only one channel can be selected at a time if not in burst mode
+ if (!burst()) LPC_ADC->ADCR &= ~0xFF;
+ //Select channel
+ LPC_ADC->ADCR |= (1 << chan);
+ }
+ else {
+ switch(pin) {
+ case p15://=p0.23 of LPC1768
+ default:
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+ break;
+ case p16://=p0.24 of LPC1768
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+ break;
+ case p17://=p0.25 of LPC1768
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+ break;
+ case p18://=p0.26 of LPC1768:
+ LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+ LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+ break;
+ case p19://=p1.30 of LPC1768
+ LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+ LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+ break;
+ case p20://=p1.31 of LPC1768
+ LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+ LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+ break;
+ }
+ LPC_ADC->ADCR &= ~(1 << chan);
+ }
+}
+//Return channel enabled/disabled state
+int ADC::setup(PinName pin) {
+ int chan;
+
+ chan = _pin_to_channel(pin);
+ return((LPC_ADC->ADCR & (1 << chan)) >> chan);
+}
+
+//Select channel already setup
+void ADC::select(PinName pin) {
+ int chan;
+
+ //Only one channel can be selected at a time if not in burst mode
+ if (!burst()) LPC_ADC->ADCR &= ~0xFF;
+ //Select channel
+ chan = _pin_to_channel(pin);
+ LPC_ADC->ADCR |= (1 << chan);
+}
+
+//Enable or disable burst mode
+void ADC::burst(int state) {
+ if ((state & 1) == 1) {
+ if (startmode(0) != 0)
+ fprintf(stderr, "Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0));
+ LPC_ADC->ADCR |= (1 << 16);
+ }
+ else
+ LPC_ADC->ADCR &= ~(1 << 16);
+}
+//Return burst mode state
+int ADC::burst(void) {
+ return((LPC_ADC->ADCR & (1 << 16)) >> 16);
+}
+
+//Set startmode and edge
+void ADC::startmode(int mode, int edge) {
+ int lpc_adc_temp;
+
+ //Reset start mode and edge bit,
+ lpc_adc_temp = LPC_ADC->ADCR & ~(0x0F << 24);
+ //Write with new values
+ lpc_adc_temp |= ((mode & 7) << 24) | ((edge & 1) << 27);
+ LPC_ADC->ADCR = lpc_adc_temp;
+}
+
+//Return startmode state according to mode_edge=0: mode and mode_edge=1: edge
+int ADC::startmode(int mode_edge){
+ switch (mode_edge) {
+ case 0:
+ default:
+ return((LPC_ADC->ADCR >> 24) & 0x07);
+ case 1:
+ return((LPC_ADC->ADCR >> 27) & 0x01);
+ }
+}
+
+//Start ADC conversion
+void ADC::start(void) {
+ startmode(1,0);
+}
+
+
+//Set interrupt enable/disable for pin to state
+void ADC::interrupt_state(PinName pin, int state) {
+ int chan;
+
+ chan = _pin_to_channel(pin);
+ if (state == 1) {
+ LPC_ADC->ADINTEN &= ~0x100;
+ LPC_ADC->ADINTEN |= 1 << chan;
+ /* Enable the ADC Interrupt */
+ NVIC_EnableIRQ(ADC_IRQn);
+ } else {
+ LPC_ADC->ADINTEN &= ~( 1 << chan );
+ //Disable interrrupt if no active pins left
+ if ((LPC_ADC->ADINTEN & 0xFF) == 0)
+ NVIC_DisableIRQ(ADC_IRQn);
+ }
+}
+
+//Return enable/disable state of interrupt for pin
+int ADC::interrupt_state(PinName pin) {
+ int chan;
+
+ chan = _pin_to_channel(pin);
+ return((LPC_ADC->ADINTEN >> chan) & 0x01);
+}
+
+
+//Attach custom interrupt handler replacing default
+void ADC::attach(void(*fptr)(void)) {
+ //* Attach IRQ
+ NVIC_SetVector(ADC_IRQn, (uint32_t)fptr);
+}
+
+//Restore default interrupt handler
+void ADC::detach(void) {
+ //* Attach IRQ
+ instance = this;
+ NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr);
+}
+
+
+//Append interrupt handler for pin to function isr
+void ADC::append(PinName pin, void(*fptr)(uint32_t value)) {
+ int chan;
+
+ chan = _pin_to_channel(pin);
+ _adc_isr[chan] = fptr;
+}
+
+//Append interrupt handler for pin to function isr
+void ADC::unappend(PinName pin) {
+ int chan;
+
+ chan = _pin_to_channel(pin);
+ _adc_isr[chan] = NULL;
+}
+
+//Unappend global interrupt handler to function isr
+void ADC::append(void(*fptr)(int chan, uint32_t value)) {
+ _adc_g_isr = fptr;
+}
+
+//Detach global interrupt handler to function isr
+void ADC::unappend() {
+ _adc_g_isr = NULL;
+}
+
+//Set ADC offset
+void offset(int offset) {
+ LPC_ADC->ADTRM &= ~(0x07 << 4);
+ LPC_ADC->ADTRM |= (offset & 0x07) << 4;
+}
+
+//Return current ADC offset
+int offset(void) {
+ return((LPC_ADC->ADTRM >> 4) & 0x07);
+}
+
+//Return value of ADC on pin
+int ADC::read(PinName pin) {
+ //Reset DONE and OVERRUN flags of interrupt handled ADC data
+ _adc_data[_pin_to_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30));
+ //Return value
+ return((_data_of_pin(pin) >> 4) & 0xFFF);
+}
+
+//Return DONE flag of ADC on pin
+int ADC::done(PinName pin) {
+ return((_data_of_pin(pin) >> 31) & 0x01);
+}
+
+//Return OVERRUN flag of ADC on pin
+int ADC::overrun(PinName pin) {
+ return((_data_of_pin(pin) >> 30) & 0x01);
+}
+
+int ADC::actual_adc_clock(void) {
+ return(_adc_clk_freq);
+}
+
+int ADC::actual_sample_rate(void) {
+ return(_adc_clk_freq / CLKS_PER_SAMPLE);
+}
diff -r 000000000000 -r 31e91bb0ef3c libs/ADC/adc.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/ADC/adc.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,131 @@
+/* mbed Library - ADC
+ * Copyright (c) 2010, sblandford
+ * released under MIT license http://mbed.org/licence/mit
+ */
+
+#ifndef MBED_ADC_H
+#define MBED_ADC_H
+
+#include "PinNames.h" // mbed.h lib
+#define XTAL_FREQ 12000000
+#define MAX_ADC_CLOCK 13000000
+#define CLKS_PER_SAMPLE 64
+
+class ADC {
+public:
+
+ //Initialize ADC with ADC maximum sample rate of
+ //sample_rate and system clock divider of cclk_div
+ //Maximum recommened sample rate is 184000
+ ADC(int sample_rate, int cclk_div);
+
+ //Enable/disable ADC on pin according to state
+ //and also select/de-select for next conversion
+ void setup(PinName pin, int state);
+
+ //Return enabled/disabled state of ADC on pin
+ int setup(PinName pin);
+
+ //Enable/disable burst mode according to state
+ void burst(int state);
+
+ //Select channel already setup
+ void select(PinName pin);
+
+ //Return burst mode enabled/disabled
+ int burst(void);
+
+ /*Set start condition and edge according to mode:
+ 0 - No start (this value should be used when clearing PDN to 0).
+ 1 - Start conversion now.
+ 2 - Start conversion when the edge selected by bit 27 occurs on the P2.10 / EINT0 / NMI pin.
+ 3 - Start conversion when the edge selected by bit 27 occurs on the P1.27 / CLKOUT /
+ USB_OVRCRn / CAP0.1 pin.
+ 4 - Start conversion when the edge selected by bit 27 occurs on MAT0.1. Note that this does
+ not require that the MAT0.1 function appear on a device pin.
+ 5 - Start conversion when the edge selected by bit 27 occurs on MAT0.3. Note that it is not
+ possible to cause the MAT0.3 function to appear on a device pin.
+ 6 - Start conversion when the edge selected by bit 27 occurs on MAT1.0. Note that this does
+ not require that the MAT1.0 function appear on a device pin.
+ 7 - Start conversion when the edge selected by bit 27 occurs on MAT1.1. Note that this does
+ not require that the MAT1.1 function appear on a device pin.
+ When mode >= 2, conversion is triggered by edge:
+ 0 - Rising edge
+ 1 - Falling edge
+ */
+ void startmode(int mode, int edge);
+
+ //Return startmode state according to mode_edge=0: mode and mode_edge=1: edge
+ int startmode(int mode_edge);
+
+ //Start ADC conversion
+ void start(void);
+
+ //Set interrupt enable/disable for pin to state
+ void interrupt_state(PinName pin, int state);
+
+ //Return enable/disable state of interrupt for pin
+ int interrupt_state(PinName pin);
+
+ //Attach custom interrupt handler replacing default
+ void attach(void(*fptr)(void));
+
+ //Restore default interrupt handler
+ void detach(void);
+
+ //Append custom interrupt handler for pin
+ void append(PinName pin, void(*fptr)(uint32_t value));
+
+ //Unappend custom interrupt handler for pin
+ void unappend(PinName pin);
+
+ //Append custom global interrupt handler
+ void append(void(*fptr)(int chan, uint32_t value));
+
+ //Unappend custom global interrupt handler
+ void unappend(void);
+
+ //Set ADC offset to a value 0-7
+ void offset(int offset);
+
+ //Return current ADC offset
+ int offset(void);
+
+ //Return value of ADC on pin
+ int read(PinName pin);
+
+ //Return DONE flag of ADC on pin
+ int done(PinName pin);
+
+ //Return OVERRUN flag of ADC on pin
+ int overrun(PinName pin);
+
+ //Return actual ADC clock
+ int actual_adc_clock(void);
+
+ //Return actual maximum sample rate
+ int actual_sample_rate(void);
+
+ //Return pin ID of ADC channel
+ PinName channel_to_pin(int chan);
+
+ //Return pin number of ADC channel
+ int channel_to_pin_number(int chan);
+
+
+private:
+ int _pin_to_channel(PinName pin);
+ uint32_t _data_of_pin(PinName pin);
+
+ int _adc_clk_freq;
+ void adcisr(void);
+ static void _adcisr(void);
+ static ADC *instance;
+
+ uint32_t _adc_data[8];
+ void(*_adc_isr[8])(uint32_t value);
+ void(*_adc_g_isr)(int chan, uint32_t value);
+ void(*_adc_m_isr)(void);
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Adc.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Adc.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,51 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "Adc.h"
+#include "libs/ADC/adc.h"
+#include "libs/Pin.h"
+
+Adc::Adc(){
+ this->adc = new ADC(1000, 1);
+}
+
+void Adc::enable_pin(Pin* pin){
+ PinName pin_name = this->_pin_to_pinname(pin);
+ this->adc->burst(1);
+ this->adc->setup(pin_name,1);
+ this->adc->interrupt_state(pin_name,1);
+}
+
+unsigned int Adc::read(Pin* pin){
+ return this->adc->read(this->_pin_to_pinname(pin));
+}
+
+PinName Adc::_pin_to_pinname(Pin* pin){
+ if( pin->port == LPC_GPIO0 && pin->pin == 23 ){
+ return p15;
+ }else if( pin->port == LPC_GPIO0 && pin->pin == 24 ){
+ return p16;
+ }else if( pin->port == LPC_GPIO0 && pin->pin == 25 ){
+ return p17;
+ }else if( pin->port == LPC_GPIO0 && pin->pin == 26 ){
+ return p18;
+ }else if( pin->port == LPC_GPIO1 && pin->pin == 30 ){
+ return p19;
+ }else if( pin->port == LPC_GPIO1 && pin->pin == 31 ){
+ return p20;
+ }else{
+ //TODO: Error
+ error("Bad adc pin");
+
+ }
+}
+
diff -r 000000000000 -r 31e91bb0ef3c libs/Adc.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Adc.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,34 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+
+#ifndef ADC_H
+#define ADC_H
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "PinNames.h" // mbed.h lib
+#include "libs/ADC/adc.h"
+#include "libs/Pin.h"
+
+class Adc : public Module{
+ public:
+ Adc();
+ void enable_pin(Pin* pin);
+ unsigned int read(Pin* pin);
+ PinName _pin_to_pinname(Pin* pin);
+
+ ADC* adc;
+};
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Config.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Config.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,150 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+using namespace std;
+#include <vector>
+#include <string>
+
+#include "libs/Kernel.h"
+#include "Config.h"
+#include "ConfigValue.h"
+#include "ConfigSource.h"
+#include "ConfigCache.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/SerialMessage.h"
+#include "libs/ConfigSources/FileConfigSource.h"
+
+
+Config::Config(){
+
+ this->config_cache_loaded = false;
+
+ // Config source for */config files
+ this->config_sources.push_back( new FileConfigSource("/local/config", LOCAL_CONFIGSOURCE_CHECKSUM) );
+ //this->config_sources.push_back( new FileConfigSource("/sd/config", SD_CONFIGSOURCE_CHECKSUM ) );
+
+ // Pre-load the config cache
+ this->config_cache_load();
+
+}
+
+void Config::on_module_loaded(){}
+
+void Config::on_console_line_received( void* argument ){}
+
+void Config::set_string( string setting, string value ){
+ ConfigValue* cv = new ConfigValue;
+ cv->found = true;
+ cv->check_sums = get_checksums(setting);
+ cv->value = value;
+
+ this->config_cache.replace_or_push_back(cv);
+
+ this->kernel->call_event(ON_CONFIG_RELOAD);
+}
+
+void Config::get_module_list(vector<uint16_t>* list, uint16_t family){
+ for( int i=1; i<this->config_cache.size(); i++){
+ ConfigValue* value = this->config_cache.at(i);
+ if( value->check_sums.size() == 3 && value->check_sums.at(2) == 29545 && value->check_sums.at(0) == family ){
+ // We found a module enable for this family, add it's number
+ list->push_back(value->check_sums.at(1));
+ }
+ }
+}
+
+
+// Command to load config cache into buffer for multiple reads during init
+void Config::config_cache_load(){
+
+ this->config_cache_clear();
+
+ // First element is a special empty ConfigValue for values not found
+ ConfigValue* result = new ConfigValue;
+ this->config_cache.push_back(result);
+ // For each ConfigSource in our stack
+ for( unsigned int i = 0; i < this->config_sources.size(); i++ ){
+ ConfigSource* source = this->config_sources[i];
+ source->transfer_values_to_cache(&this->config_cache);
+ }
+
+ this->config_cache_loaded = true;
+}
+
+// Command to clear the config cache after init
+void Config::config_cache_clear(){
+ while( ! this->config_cache.empty() ){
+ delete this->config_cache.back();
+ this->config_cache.pop_back();
+ }
+ this->config_cache_loaded = false;
+}
+
+
+ConfigValue* Config::value(uint16_t check_sum_a, uint16_t check_sum_b, uint16_t check_sum_c ){
+ vector<uint16_t> check_sums;
+ check_sums.push_back(check_sum_a);
+ check_sums.push_back(check_sum_b);
+ check_sums.push_back(check_sum_c);
+ return this->value(check_sums);
+}
+
+ConfigValue* Config::value(uint16_t check_sum_a, uint16_t check_sum_b){
+ vector<uint16_t> check_sums;
+ check_sums.push_back(check_sum_a);
+ check_sums.push_back(check_sum_b);
+ return this->value(check_sums);
+}
+
+ConfigValue* Config::value(uint16_t check_sum){
+ vector<uint16_t> check_sums;
+ check_sums.push_back(check_sum);
+ return this->value(check_sums);
+}
+
+// Get a value from the configuration as a string
+// Because we don't like to waste space in Flash with lengthy config parameter names, we take a checksum instead so that the name does not have to be stored
+// See get_checksum
+ConfigValue* Config::value(vector<uint16_t> check_sums){
+ ConfigValue* result = this->config_cache[0];
+ //if( this->has_config_file() == false ){
+ // return result;
+ //}
+ // Check if the config is cached, and load it temporarily if it isn't
+ bool cache_preloaded = this->config_cache_loaded;
+ if( !cache_preloaded ){ this->config_cache_load(); }
+
+ for( int i=1; i<this->config_cache.size(); i++){
+ // If this line matches the checksum
+ bool match = true;
+ for( unsigned int j = 0; j < check_sums.size(); j++ ){
+ uint16_t checksum_node = check_sums[j];
+
+ //printf("%u(%s) against %u\r\n", get_checksum(key_node), key_node.c_str(), checksum_node);
+ if(this->config_cache[i]->check_sums[j] != checksum_node ){
+ match = false;
+ break;
+ }
+ }
+ if( match == false ){
+ //printf("continue\r\n");
+ continue;
+ }
+ result = this->config_cache[i];
+ break;
+ }
+
+ if( !cache_preloaded ){
+ this->config_cache_clear();
+ }
+ return result;
+}
+
+
+
diff -r 000000000000 -r 31e91bb0ef3c libs/Config.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Config.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,53 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+#include "libs/Kernel.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+#include "ConfigValue.h"
+#include "ConfigCache.h"
+#include "ConfigSource.h"
+#include "libs/ConfigSources/FileConfigSource.h"
+
+#define error(...) (fprintf(stderr, __VA_ARGS__), exit(1))
+
+using namespace std;
+#include <vector>
+#include <string>
+#include <stdio.h>
+
+#define LOCAL_CONFIGSOURCE_CHECKSUM 13581
+#define SD_CONFIGSOURCE_CHECKSUM 19415
+
+
+class Config : public Module {
+ public:
+ Config();
+
+ virtual void on_module_loaded();
+ virtual void on_console_line_received( void* argument );
+ void config_cache_load();
+ void config_cache_clear();
+ void set_string( string setting , string value);
+
+ ConfigValue* value(uint16_t check_sum);
+ ConfigValue* value(uint16_t check_sum_a, uint16_t check_sum_b);
+ ConfigValue* value(uint16_t check_sum_a, uint16_t check_sum_b, uint16_t check_sum_c );
+ ConfigValue* value(vector<uint16_t> check_sums );
+
+ void get_module_list(vector<uint16_t>* list, uint16_t family);
+
+ bool has_characters(uint16_t check_sum, string str );
+
+ ConfigCache config_cache; // A cache in which ConfigValues are kept
+ vector<ConfigSource*> config_sources; // A list of all possible coniguration sources
+ bool config_cache_loaded; // Whether or not the cache is currently popluated
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/ConfigCache.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/ConfigCache.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,54 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CONFIGCACHE_H
+#define CONFIGCACHE_H
+
+using namespace std;
+#include <vector>
+
+#include "ConfigValue.h"
+
+class ConfigCache : public std::vector<ConfigValue*> {
+ public:
+ ConfigCache(){}
+
+ // If we find an existing value, replace it, otherwise, push it at the back of the list
+ void replace_or_push_back(ConfigValue* new_value){
+
+ bool value_exists = false;
+ // For each already existing element
+ for( int i=1; i<this->size(); i++){
+ // If this configvalue matches the checksum
+ bool match = true;
+ for( unsigned int j = 0; j < new_value->check_sums.size(); j++ ){
+ uint16_t checksum_node = new_value->check_sums[j];
+ if(this->at(i)->check_sums[j] != checksum_node ){
+ match = false;
+ break;
+ }
+ }
+ if( match == false ){ continue; }
+ value_exists = true;
+ // Replace with the provided value
+ delete this->at(i);
+ this->at(i) = new_value;
+ break;
+ }
+
+ // Value does not already exists, add to the list
+ if( value_exists == false ){
+ this->push_back(new_value);
+ }
+
+ }
+
+};
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/ConfigSource.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/ConfigSource.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,34 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CONFIGSOURCE_H
+#define CONFIGSOURCE_H
+
+using namespace std;
+#include <vector>
+#include <string>
+#include "ConfigValue.h"
+#include "ConfigCache.h"
+
+class ConfigValue;
+
+class ConfigSource {
+ public:
+ ConfigSource(){}
+
+ // Read each value, and append it as a ConfigValue to the config_cache we were passed
+ virtual void transfer_values_to_cache( ConfigCache* ){}
+ virtual bool is_named( uint16_t check_sum ){return false;}
+ virtual void write( string setting, string value ){}
+ virtual string read( vector<uint16_t> check_sums ){return "";}
+
+ uint16_t name_checksum;
+};
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/ConfigSources/FileConfigSource.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/ConfigSources/FileConfigSource.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,177 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Kernel.h"
+#include "ConfigValue.h"
+#include "FileConfigSource.h"
+#include "ConfigCache.h"
+using namespace std;
+#include <string>
+
+FileConfigSource::FileConfigSource(string config_file, uint16_t name_checksum){
+ this->name_checksum = name_checksum;
+ this->config_file = config_file;
+ this->config_file_found = false;
+}
+
+// Transfer all values found in the file to the passed cache
+void FileConfigSource::transfer_values_to_cache( ConfigCache* cache ){
+ // Default empty value
+ ConfigValue* result = new ConfigValue;
+ if( this->has_config_file() == false ){
+ return;
+ }
+ // Open the config file ( find it if we haven't already found it )
+ FILE *lp = fopen(this->get_config_file().c_str(), "r");
+ string buffer;
+ int c;
+ // For each line
+ do {
+ c = fgetc (lp);
+ if (c == '\n' || c == EOF){
+ // We have a new line
+ if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments
+ if( buffer.length() < 3 ){ buffer.clear(); continue; } //Ignore empty lines
+ size_t begin_key = buffer.find_first_not_of(" ");
+ size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key));
+ string key = buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key).append(" ");
+ vector<uint16_t> check_sums = get_checksums(key);
+
+ result = new ConfigValue;
+ result->found = true;
+ result->check_sums = check_sums;
+ result->value = buffer.substr(begin_value, buffer.find_first_of("\r\n# ", begin_value+1)-begin_value);
+
+ // Append the newly found value to the cache we were passed
+ cache->replace_or_push_back(result);
+
+ buffer.clear();
+ }else{
+ buffer += c;
+ }
+ } while (c != EOF);
+ fclose(lp);
+
+}
+
+// Return true if the check_sums match
+bool FileConfigSource::is_named( uint16_t check_sum ){
+ return check_sum == this->name_checksum;
+}
+
+// Write a config setting to the file
+void FileConfigSource::write( string setting, string value ){
+/*
+ // Open the config file ( find it if we haven't already found it )
+ FILE *lp = fopen(this->get_config_file().c_str(), "r+");
+ string buffer;
+ int c;
+ // For each line
+ do {
+ c = fgetc (lp);
+ if (c == '\n' || c == EOF){
+ // We have a new line
+ if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments
+ if( buffer.length() < 3 ){ buffer.clear(); continue; } //Ignore empty lines
+ size_t begin_key = buffer.find_first_not_of(" ");
+ size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key));
+ // If this line matches the checksum
+ string candidate = buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key);
+ if( candidate.compare(setting) != 0 ){ buffer.clear(); continue; }
+ int free_space = int(int(buffer.find_first_of("\r\n#", begin_value+1))-begin_value);
+ if( int(value.length()) >= free_space ){
+ //this->kernel->serial->printf("ERROR: Not enough room for value\r\n");
+ fclose(lp);
+ return;
+ }
+ // Update value
+ for( int i = value.length(); i < free_space; i++){ value += " "; }
+ fpos_t pos;
+ fgetpos( lp, &pos );
+ int start = pos - buffer.length() + begin_value - 1;
+ fseek(lp, start, SEEK_SET);
+ fputs(value.c_str(), lp);
+ fclose(lp);
+ return;
+ }else{
+ buffer += c;
+ }
+ } while (c != EOF);
+ fclose(lp);
+ //this->kernel->serial->printf("ERROR: configuration key not found\r\n");
+ */
+}
+
+// Return the value for a specific checksum
+string FileConfigSource::read( vector<uint16_t> check_sums ){
+
+ string value = "";
+
+ if( this->has_config_file() == false ){return value;}
+ // Open the config file ( find it if we haven't already found it )
+ FILE *lp = fopen(this->get_config_file().c_str(), "r");
+ string buffer;
+ int c;
+ // For each line
+ do {
+ c = fgetc (lp);
+ if (c == '\n' || c == EOF){
+ // We have a new line
+ if( buffer[0] == '#' ){ buffer.clear(); continue; } // Ignore comments
+ if( buffer.length() < 3 ){ buffer.clear(); continue; } //Ignore empty lines
+ size_t begin_key = buffer.find_first_not_of(" ");
+ size_t begin_value = buffer.find_first_not_of(" ", buffer.find_first_of(" ", begin_key));
+ string key = buffer.substr(begin_key, buffer.find_first_of(" ", begin_key) - begin_key).append(" ");
+ vector<uint16_t> line_checksums = get_checksums(key);
+
+ if(check_sums == line_checksums){
+ value = buffer.substr(begin_value, buffer.find_first_of("\r\n# ", begin_value+1)-begin_value);
+ break;
+ }
+
+ buffer.clear();
+ }else{
+ buffer += c;
+ }
+ } while (c != EOF);
+ fclose(lp);
+
+ return value;
+}
+
+// Return wether or not we have a readable config file
+bool FileConfigSource::has_config_file(){
+ if( this->config_file_found ){ return true; }
+ this->try_config_file(this->config_file);
+ if( this->config_file_found ){
+ return true;
+ }else{
+ return false;
+ }
+
+}
+
+// Tool function for get_config_file
+inline void FileConfigSource::try_config_file(string candidate){
+ if(file_exists(candidate)){
+ this->config_file_found = true;
+ }
+}
+
+// Get the filename for the config file
+string FileConfigSource::get_config_file(){
+ if( this->config_file_found ){ return this->config_file; }
+ if( this->has_config_file() ){
+ return this->config_file;
+ }else{
+ printf("ERROR: no config file found\r\n");
+ }
+}
+
+
+
+
diff -r 000000000000 -r 31e91bb0ef3c libs/ConfigSources/FileConfigSource.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/ConfigSources/FileConfigSource.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,39 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FILECONFIGSOURCE_H
+#define FILECONFIGSOURCE_H
+
+#include "ConfigValue.h"
+#include "ConfigSource.h"
+#include "ConfigCache.h"
+
+using namespace std;
+#include <string>
+
+#define FILE_CONFIGSOURCE_CHECKSUM 5281 // "file"
+
+class FileConfigSource : public ConfigSource {
+ public:
+ FileConfigSource(string config_file = "/sd/config", uint16_t name_checksum = FILE_CONFIGSOURCE_CHECKSUM);
+ virtual void transfer_values_to_cache( ConfigCache* cache );
+ virtual bool is_named( uint16_t check_sum );
+ virtual void write( string setting, string value );
+ virtual string read( vector<uint16_t> check_sums );
+ bool has_config_file();
+ void try_config_file(string candidate);
+ string get_config_file();
+
+ string config_file; // Path to the config file
+ bool config_file_found; // Wether or not the config file's location is known
+
+
+};
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/ConfigValue.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/ConfigValue.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,115 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CONFIGVALUE_H
+#define CONFIGVALUE_H
+
+#include "libs/Kernel.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+
+
+#define error(...) (fprintf(stderr, __VA_ARGS__), exit(1))
+
+using namespace std;
+#include <vector>
+#include <string>
+#include <stdio.h>
+
+
+class ConfigValue{
+ public:
+ ConfigValue(){
+ this->found = false;
+ this->default_set = false;
+ };
+
+ ConfigValue* required(){
+ if( this->found == true ){
+ return this;
+ }else{
+ error("could not find config setting with checksum %u, please see http://smoothieware.org/configuring-smoothie\r\n", this->check_sum );
+ return NULL;
+ }
+ }
+
+ double as_number(){
+ if( this->found == false && this->default_set == true ){
+ return this->default_double;
+ }else{
+ double result = atof(remove_non_number(this->value).c_str());
+ if( result == 0.0 && this->value.find_first_not_of("0.") != string::npos ){
+ error("config setting with value '%s' is not a valid number, please see http://smoothieware.org/configuring-smoothie\r\n", this->value.c_str() );
+ }
+ return result;
+ }
+
+ }
+
+ std::string as_string(){
+ if( this->found == false && this->default_set == true ){
+ return this->default_string;
+ }else{
+ return this->value;
+ }
+ }
+
+ bool as_bool(){
+ if( this->found == false && this->default_set == true ){
+ return this->default_double;
+ }else{
+ if( this->value.find_first_of("t1") != string::npos ){
+ return true;
+ }else{
+ return false;
+ }
+ }
+ }
+
+ Pin* as_pin(){
+ Pin* pin = new Pin();
+ pin->from_string(this->as_string());
+ return pin;
+ }
+
+ ConfigValue* by_default(double val){
+ this->default_set = true;
+ this->default_double = val;
+ return this;
+ }
+
+ ConfigValue* by_default(std::string val){
+ this->default_set = true;
+ this->default_string = val;
+ return this;
+ }
+
+ bool has_characters( string mask ){
+ if( this->value.find_first_of(mask) != string::npos ){ return true; }else{ return false; }
+ }
+
+ bool is_inverted(){
+ return this->has_characters(string("!"));
+ }
+
+ string value;
+ vector<uint16_t> check_sums;
+ uint16_t check_sum;
+ bool found;
+ bool default_set;
+ double default_double;
+ string default_string;
+};
+
+
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Digipot.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Digipot.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,47 @@
+#ifndef DIGIPOT_H
+#define DIGIPOT_H
+
+#include "libs/Kernel.h"
+#include "I2C.h" // mbed.h lib
+#include "libs/utils.h"
+#include <string>
+#include <math.h>
+
+class Digipot{
+ public:
+ Digipot(){ }
+
+ char current_to_wiper( double current ){
+ return char(ceil(double((113.33*current))));
+ }
+
+ void i2c_send( char first, char second, char third ){
+ this->i2c->start();
+ this->i2c->write(first);
+ this->i2c->write(second);
+ this->i2c->write(third);
+ this->i2c->stop();
+ }
+
+ void set_current( int channel, double current ){
+
+ current = min( max( current, 0.0L ), 2.0L );
+
+ // I2C com
+ this->i2c = new mbed::I2C(p9, p10);
+
+ // Initial setup
+ this->i2c_send( 0x58, 0x40, 0xff );
+ this->i2c_send( 0x58, 0xA0, 0xff );
+
+ // Set actual wiper value
+ char adresses[4] = { 0x00, 0x10, 0x60, 0x70 };
+ this->i2c_send( 0x58, adresses[channel], this->current_to_wiper(current) );
+
+ }
+
+ mbed::I2C* i2c;
+};
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/FPointer.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/FPointer.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,162 @@
+/*
+Copyright (c) 2011 Andy Kirkham
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef AJK_FPOINTER_H
+#define AJK_FPOINTER_H
+#ifndef NULL
+#define NULL 0
+#endif
+namespace AjK {
+
+class FPointerDummy;
+
+/** FPointer - Adds callbacks that take and return a 32bit uint32_t data type.
+*
+* The Mbed library supplies a callback using the FunctionPointer object as
+* defined in FunctionPointer.h However, this callback system does not allow
+* the caller to pass a value to the callback. Likewise, the callback itself
+* cannot return a value.
+*
+* FPointer operates in the same way but allows the callback function to be
+* passed one arg, a uint32_t value. Additionally, the callback can return
+* a single uint32_t value. The reason for using uint32_t is that the Mbed
+* and the microcontroller (LPC1768) have a natural data size of 32bits and
+* this means we can use the uint32_t as a pointer. See example1.h for more
+* information. This example passes an "int" by passing a pointer to that
+* int as a 32bit value. Using this technique you can pass any value you like.
+* All you have to do is pass a pointer to your value cast to (uint32_t). Your
+* callback can the deference it to get the original value.
+*
+* example2.h shows how to do the same thing but demostrates how to specify
+* the callback into a class object/method.
+*
+* Finally, example3.h shows how to pass multiple values. In this example we
+* define a data structure and in the callback we pass a pointer to that
+* data structure thus allowing the callback to again get the values.
+*
+* Note, when passing pointers to variables to the callback, if the callback
+* function/method changes that variable's value then it will also change the
+* value the caller sees. If C pointers are new to you, you are strongly
+* advised to read up on the subject. It's pointers that often get beginners
+* into trouble when mis-used.
+*
+* @see example1.h
+* @see example2.h
+* @see example3.h
+* @see http://mbed.org/handbook/C-Data-Types
+* @see http://mbed.org/projects/libraries/svn/mbed/trunk/FunctionPointer.h
+*/
+class FPointer {
+
+protected:
+
+ //! C callback function pointer.
+ uint32_t (*c_callback)(uint32_t);
+
+ //! C++ callback object/method pointer (the object part).
+ FPointerDummy *obj_callback;
+
+ //! C++ callback object/method pointer (the method part).
+ uint32_t (FPointerDummy::*method_callback)(uint32_t);
+
+public:
+
+ /** Constructor
+*/
+ FPointer() {
+ c_callback = NULL;
+ obj_callback = NULL;
+ method_callback = NULL;
+ }
+
+ /** attach - Overloaded attachment function.
+*
+* Attach a C type function pointer as the callback.
+*
+* Note, the callback function prototype must be:-
+* @code
+* uint32_t myCallbackFunction(uint32_t);
+* @endcode
+* @param A C function pointer to call.
+*/
+ void attach(uint32_t (*function)(uint32_t) = 0) { c_callback = function; }
+
+ /** attach - Overloaded attachment function.
+*
+* Attach a C++ type object/method pointer as the callback.
+*
+* Note, the callback method prototype must be:-
+* @code
+* public:
+* uint32_t myCallbackFunction(uint32_t);
+* @endcode
+* @param A C++ object pointer.
+* @param A C++ method within the object to call.
+*/
+ template<class T>
+ void attach(T* item, uint32_t (T::*method)(uint32_t)) {
+ obj_callback = (FPointerDummy *)item;
+ method_callback = (uint32_t (FPointerDummy::*)(uint32_t))method;
+ }
+
+ /** call - Overloaded callback initiator.
+*
+* call the callback function.
+*
+* @param uint32_t The value to pass to the callback.
+* @return uint32_t The value the callback returns.
+*/
+ uint32_t call(uint32_t arg) {
+ if (c_callback != NULL) {
+ return (*c_callback)(arg);
+ }
+ else {
+ if (obj_callback != NULL && method_callback != NULL) {
+ return (obj_callback->*method_callback)(arg);
+ }
+ }
+ return (uint32_t)NULL;
+ }
+
+ /** call - Overloaded callback initiator.
+*
+* Call the callback function without passing an argument.
+* The callback itself is passed NULL. Note, the callback
+* prototype should still be <b>uint32_t callback(uint32_t)</b>.
+*
+* @return uint32_t The value the callback returns.
+*/
+ uint32_t call(void) {
+ if (c_callback != NULL) {
+ return (*c_callback)((uint32_t)NULL);
+ }
+ else {
+ if (obj_callback != NULL && method_callback != NULL) {
+ return (obj_callback->*method_callback)((uint32_t)NULL);
+ }
+ }
+ return (uint32_t)NULL;
+ }
+};
+
+}; // namespace AjK ends
+
+using namespace AjK;
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Hook.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Hook.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,6 @@
+extern "C"{
+ #include <stdint.h>
+}
+#include "Hook.h"
+
+Hook::Hook(){}
diff -r 000000000000 -r 31e91bb0ef3c libs/Hook.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Hook.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,12 @@
+#ifndef HOOK_H
+#define HOOK_H
+#include "libs/FPointer.h"
+
+class Hook : public FPointer {
+ public:
+ Hook();
+ double frequency;
+ double counter;
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Kernel.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Kernel.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,97 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using namespace std;
+#include <vector>
+#include "libs/Kernel.h"
+#include "libs/Module.h"
+#include "libs/Config.h"
+#include "libs/nuts_bolts.h"
+#include "libs/SlowTicker.h"
+#include "libs/Adc.h"
+#include "libs/Digipot.h"
+#include "libs/Pauser.h"
+
+#include "modules/communication/SerialConsole.h"
+#include "modules/communication/GcodeDispatch.h"
+#include "modules/robot/Planner.h"
+#include "modules/robot/Robot.h"
+#include "modules/robot/Stepper.h"
+#include "modules/robot/Player.h"
+
+
+// List of callback functions, ordered as their corresponding events
+const ModuleCallback kernel_callback_functions[NUMBER_OF_DEFINED_EVENTS] = {
+ &Module::on_main_loop,
+ &Module::on_console_line_received,
+ &Module::on_gcode_received,
+ &Module::on_stepper_wake_up,
+ &Module::on_gcode_execute,
+ &Module::on_speed_change,
+ &Module::on_block_begin,
+ &Module::on_block_end,
+ &Module::on_config_reload,
+ &Module::on_play,
+ &Module::on_pause,
+ &Module::on_idle
+};
+
+#define baud_rate_setting_checksum 10922
+#define uart0_checksum 16877
+
+// The kernel is the central point in Smoothie : it stores modules, and handles event calls
+Kernel::Kernel(){
+
+ // Config first, because we need the baud_rate setting before we start serial
+ this->config = new Config();
+
+ // Serial second, because the other modules might want to say something
+ this->serial = new SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(9600)->as_number());
+
+ this->add_module( this->config );
+ this->add_module( this->serial );
+
+ // HAL stuff
+ this->slow_ticker = new SlowTicker();
+ this->step_ticker = new StepTicker();
+ this->adc = new Adc();
+ this->digipot = new Digipot();
+
+ // LPC17xx-specific
+ NVIC_SetPriority(TIMER0_IRQn, 1);
+ NVIC_SetPriority(TIMER2_IRQn, 2);
+
+ // Core modules
+ this->add_module( this->gcode_dispatch = new GcodeDispatch() );
+ this->add_module( this->robot = new Robot() );
+ this->add_module( this->stepper = new Stepper() );
+ this->add_module( this->planner = new Planner() );
+ this->add_module( this->player = new Player() );
+ this->add_module( this->pauser = new Pauser() );
+}
+
+void Kernel::add_module(Module* module){
+ module->kernel = this;
+ module->on_module_loaded();
+ module->register_for_event(ON_CONFIG_RELOAD);
+}
+
+void Kernel::register_for_event(unsigned int id_event, Module* module){
+ this->hooks[id_event].push_back(module);
+}
+
+void Kernel::call_event(unsigned int id_event){
+ for(unsigned int i=0; i < this->hooks[id_event].size(); i++){
+ (this->hooks[id_event][i]->*kernel_callback_functions[id_event])(this);
+ }
+}
+
+void Kernel::call_event(unsigned int id_event, void * argument){
+ for(unsigned int i=0; i < this->hooks[id_event].size(); i++){
+ (this->hooks[id_event][i]->*kernel_callback_functions[id_event])(argument);
+ }
+}
diff -r 000000000000 -r 31e91bb0ef3c libs/Kernel.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Kernel.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,77 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef KERNEL_H
+#define KERNEL_H
+#include "libs/Module.h"
+#include "libs/Config.h"
+#include "libs/SlowTicker.h"
+#include "libs/StepTicker.h"
+#include "libs/Adc.h"
+#include "libs/Digipot.h"
+#include "libs/Pauser.h"
+#include "modules/communication/SerialConsole.h"
+#include "modules/communication/GcodeDispatch.h"
+#include "modules/robot/Planner.h"
+#include "modules/robot/Robot.h"
+#include "modules/robot/Stepper.h"
+
+// See : http://smoothieware.org/listofevents
+#define NUMBER_OF_DEFINED_EVENTS 12
+#define ON_MAIN_LOOP 0
+#define ON_CONSOLE_LINE_RECEIVED 1
+#define ON_GCODE_RECEIVED 2
+#define ON_STEPPER_WAKE_UP 3 //TODO :Â Remove the need for this event, then this event itself eg:Â have planner call stepper directly
+#define ON_GCODE_EXECUTE 4
+#define ON_SPEED_CHANGE 5
+#define ON_BLOCK_BEGIN 6
+#define ON_BLOCK_END 7
+#define ON_CONFIG_RELOAD 8
+#define ON_PLAY 9
+#define ON_PAUSE 10
+#define ON_IDLE 11
+
+
+using namespace std;
+#include <vector>
+
+typedef void (Module::*ModuleCallback)(void * argument);
+
+//Module manager
+class Module;
+class Player;
+class SlowTicker;
+class Kernel {
+ public:
+ Kernel();
+ void add_module(Module* module);
+ void register_for_event(unsigned int id_event, Module* module);
+ void call_event(unsigned int id_event);
+ void call_event(unsigned int id_event, void * argument);
+
+ // These modules are aviable to all other modules
+ SerialConsole* serial;
+ GcodeDispatch* gcode_dispatch;
+ Robot* robot;
+ Stepper* stepper;
+ Planner* planner;
+ Config* config;
+ Player* player;
+ Pauser* pauser;
+
+ int debug;
+ SlowTicker* slow_ticker;
+ StepTicker* step_ticker;
+ Adc* adc;
+ Digipot* digipot;
+
+ private:
+ vector<Module*> hooks[NUMBER_OF_DEFINED_EVENTS]; // When a module asks to be called for a specific event ( a hook ), this is where that request is remembered
+
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Module.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Module.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,31 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+
+Module::Module(){ }
+
+void Module::on_module_loaded(){
+}
+
+void Module::register_for_event(int event_id){
+ this->kernel->register_for_event(event_id, this);
+}
+
+void Module::on_main_loop( void * argument){}
+void Module::on_console_line_received( void * argument){}
+void Module::on_gcode_received( void * argument){}
+void Module::on_stepper_wake_up( void * argument){}
+void Module::on_gcode_execute( void * argument){}
+void Module::on_speed_change( void * argument){}
+void Module::on_block_begin( void * argument){}
+void Module::on_block_end( void * argument){}
+void Module::on_config_reload( void * argument){}
+void Module::on_play( void * argument){}
+void Module::on_pause( void * argument){}
+void Module::on_idle( void * argument){}
diff -r 000000000000 -r 31e91bb0ef3c libs/Module.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Module.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,37 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef MODULE_H
+#define MODULE_H
+
+#include <string>
+using std::string;
+
+// Module base class
+// All modules must extend this class, see http://smoothieware.org/moduleexample
+class Kernel;
+class Module {
+ public:
+ Module();
+ virtual void on_module_loaded();
+ virtual void register_for_event(int event_id);
+ virtual void on_main_loop(void * argument);
+ virtual void on_console_line_received( void * argument);
+ virtual void on_gcode_received( void * argument);
+ virtual void on_gcode_execute( void * argument);
+ virtual void on_stepper_wake_up( void * argument);
+ virtual void on_speed_change( void * argument);
+ virtual void on_block_begin( void * argument);
+ virtual void on_block_end( void * argument);
+ virtual void on_config_reload( void * argument);
+ virtual void on_play( void * argument);
+ virtual void on_pause( void * argument);
+ virtual void on_idle( void * argument);
+ Kernel * kernel;
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Pauser.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Pauser.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,26 @@
+#include "libs/Kernel.h"
+#include "Pauser.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+using namespace std;
+
+Pauser::Pauser(){}
+
+void Pauser::on_module_loaded(){
+ this->counter = 0;
+}
+
+void Pauser::take(){
+ this->counter++;
+ if( this->counter == 1 ){
+ this->kernel->call_event(ON_PAUSE, &this->counter);
+ }
+}
+
+void Pauser::release(){
+ this->counter--;
+ if( this->counter == 0 ){
+ this->kernel->call_event(ON_PLAY, &this->counter);
+ }
+}
diff -r 000000000000 -r 31e91bb0ef3c libs/Pauser.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Pauser.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,18 @@
+#ifndef PAUSER_H
+#define PAUSER_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+
+class Pauser : public Module {
+ public:
+ Pauser();
+ virtual void on_module_loaded();
+ void take();
+ void release();
+
+ unsigned short counter;
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/Pin.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/Pin.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,64 @@
+#ifndef PIN_H
+#define PIN_H
+
+#include <stdlib.h>
+#include "mbed.h" // smoothed mbed.h lib
+#include "libs/Kernel.h"
+#include "libs/utils.h"
+#include <string>
+
+class Pin{
+ public:
+ Pin(){ }
+
+ Pin* from_string(std::string value){
+ LPC_GPIO_TypeDef* gpios[5] ={LPC_GPIO0,LPC_GPIO1,LPC_GPIO2,LPC_GPIO3,LPC_GPIO4};
+ this->port_number = atoi(value.substr(0,1).c_str());
+ this->port = gpios[this->port_number];
+ this->inverting = ( value.find_first_of("!")!=string::npos ? true : false );
+ this->pin = atoi( value.substr(2, value.size()-2-(this->inverting?1:0)).c_str() );
+ return this;
+ }
+
+ inline Pin* as_output(){
+ this->port->FIODIR |= 1<<this->pin;
+ return this;
+ }
+
+ inline Pin* as_input(){
+ this->port->FIODIR &= ~(1<<this->pin);
+ return this;
+ }
+
+ inline Pin* as_open_drain(){
+ if( this->port_number == 0 ){ LPC_PINCON->PINMODE_OD0 |= (1<<this->pin); }
+ if( this->port_number == 1 ){ LPC_PINCON->PINMODE_OD1 |= (1<<this->pin); }
+ if( this->port_number == 2 ){ LPC_PINCON->PINMODE_OD2 |= (1<<this->pin); }
+ if( this->port_number == 3 ){ LPC_PINCON->PINMODE_OD3 |= (1<<this->pin); }
+ if( this->port_number == 4 ){ LPC_PINCON->PINMODE_OD4 |= (1<<this->pin); }
+ return this;
+ }
+
+ inline bool get(){
+ return this->inverting ^ (( this->port->FIOPIN >> this->pin ) & 1);
+ }
+
+ inline void set(bool value){
+ value = this->inverting ^ value;
+ if( value ){
+ this->port->FIOSET = 1 << this->pin;
+ }else{
+ this->port->FIOCLR = 1 << this->pin;
+ }
+ }
+
+ bool inverting;
+ LPC_GPIO_TypeDef* port;
+ char port_number;
+ char pin;
+};
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/RingBuffer.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/RingBuffer.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,103 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+
+ With chucks taken from http://en.wikipedia.org/wiki/Circular_buffer, see licence there also
+*/
+
+#ifndef RINGBUFFER_H
+#define RINGBUFFER_H
+
+
+template<class kind, int length> class RingBuffer {
+ public:
+ RingBuffer();
+ int size();
+ int capacity();
+ int next_block_index(int index);
+ int prev_block_index(int index);
+ void push_back(kind object);
+ void pop_front(kind &object);
+ void get( int index, kind &object);
+ kind* get_ref( int index);
+ void delete_first();
+
+ kind buffer[length];
+ int head;
+ int tail;
+};
+
+
+template<class kind, int length> RingBuffer<kind, length>::RingBuffer(){
+ this->head = this->tail = 0;
+}
+
+template<class kind, int length> int RingBuffer<kind, length>::capacity(){
+ return length-1;
+}
+
+template<class kind, int length> int RingBuffer<kind, length>::size(){
+return((this->head>this->tail)?length:0)+this->tail-head;
+}
+
+template<class kind, int length> int RingBuffer<kind, length>::next_block_index(int index){
+ index++;
+ if (index == length) { index = 0; }
+ return(index);
+}
+
+template<class kind, int length> int RingBuffer<kind, length>::prev_block_index(int index){
+ if (index == 0) { index = length; }
+ index--;
+ return(index);
+}
+
+template<class kind, int length> void RingBuffer<kind, length>::push_back(kind object){
+ this->buffer[this->tail] = object;
+ this->tail = (tail+1)&(length-1);
+}
+
+template<class kind, int length> void RingBuffer<kind, length>::get(int index, kind &object){
+ int j= 0;
+ int k= this->head;
+ while (k != this->tail){
+ if (j == index) break;
+ j++;
+ k= (k + 1) & (length - 1);
+ }
+ if (k == this->tail){
+ //return NULL;
+ }
+ object = this->buffer[k];
+}
+
+
+template<class kind, int length> kind* RingBuffer<kind, length>::get_ref(int index){
+ int j= 0;
+ int k= this->head;
+ while (k != this->tail){
+ if (j == index) break;
+ j++;
+ k= (k + 1) & (length - 1);
+ }
+ if (k == this->tail){
+ return NULL;
+ }
+ return &(this->buffer[k]);
+}
+
+template<class kind, int length> void RingBuffer<kind, length>::pop_front(kind &object){
+ object = this->buffer[this->head];
+ this->head = (this->head+1)&(length-1);
+}
+
+template<class kind, int length> void RingBuffer<kind, length>::delete_first(){
+ //kind dummy;
+ //this->pop_front(dummy);
+ this->head = (this->head+1)&(length-1);
+}
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/RingBuffer.o Binary file libs/RingBuffer.o has changed
diff -r 000000000000 -r 31e91bb0ef3c libs/SerialMessage.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/SerialMessage.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,8 @@
+#ifndef SERIALMESSAGE_H
+#define SERIALMESSAGE_H
+#include "libs/StreamOutput.h"
+struct SerialMessage {
+ StreamOutput* stream;
+ std::string message;
+};
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/SlowTicker.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/SlowTicker.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,53 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "SlowTicker.h"
+#include "libs/Hook.h"
+#include "system_LPC17xx.h" // mbed.h lib
+
+
+SlowTicker* global_slow_ticker;
+
+SlowTicker::SlowTicker(){
+ this->max_frequency = 1;
+ global_slow_ticker = this;
+ LPC_SC->PCONP |= (1 << 22); // Power Ticker ON
+ LPC_TIM2->MR0 = 10000; // Initial dummy value for Match Register
+ LPC_TIM2->MCR = 3; // Match on MR0, reset on MR0
+ LPC_TIM2->TCR = 1; // Enable interrupt
+ NVIC_EnableIRQ(TIMER2_IRQn); // Enable interrupt handler
+}
+
+void SlowTicker::set_frequency( int frequency ){
+ LPC_TIM2->MR0 = (SystemCoreClock/4)/frequency; // SystemCoreClock/4 = Timer increments in a second
+ LPC_TIM2->TCR = 3; // Reset
+ LPC_TIM2->TCR = 1; // Reset
+}
+
+void SlowTicker::tick(){
+ for (int i=0; i<this->hooks.size(); i++){
+ Hook* hook = this->hooks.at(i);
+ hook->counter += ( hook->frequency / this->max_frequency );
+ if( hook->counter > 0 ){
+ hook->counter-=1;
+ hook->call();
+ }
+ }
+}
+
+extern "C" void TIMER2_IRQHandler (void){
+ if((LPC_TIM2->IR >> 0) & 1){ // If interrupt register set for MR0
+ LPC_TIM2->IR |= 1 << 0; // Reset it
+ global_slow_ticker->tick();
+ }
+}
+
diff -r 000000000000 -r 31e91bb0ef3c libs/SlowTicker.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/SlowTicker.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,46 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+
+#ifndef SLOWTICKER_H
+#define SLOWTICKER_H
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "libs/Hook.h"
+
+class SlowTicker : public Module{
+ public:
+ SlowTicker();
+ void set_frequency( int frequency );
+ void tick();
+ // For some reason this can't go in the .cpp, see : http://mbed.org/forum/mbed/topic/2774/?page=1#comment-14221
+ template<typename T> void attach( double frequency, T *optr, uint32_t ( T::*fptr )( uint32_t ) ){
+ Hook* hook = new Hook();
+ hook->frequency = frequency;
+ hook->attach(optr, fptr);
+ hook->counter = -2;
+ if( frequency > this->max_frequency ){
+ this->max_frequency = frequency;
+ }
+ this->set_frequency(this->max_frequency);
+ this->hooks.push_back(hook);
+ }
+
+ vector<Hook*> hooks;
+ double max_frequency;
+};
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/StepTicker.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/StepTicker.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,65 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "StepTicker.h"
+#include "system_LPC17xx.h" // mbed.h lib
+
+
+StepTicker* global_step_ticker;
+
+StepTicker::StepTicker(){
+ global_step_ticker = this;
+ LPC_TIM0->MR0 = 1000000; // Initial dummy value for Match Register
+ LPC_TIM0->MCR = 11; // Match on MR0, reset on MR0, match on MR1
+ LPC_TIM0->TCR = 1; // Enable interrupt
+ NVIC_EnableIRQ(TIMER0_IRQn); // Enable interrupt handler
+}
+
+void StepTicker::set_frequency( double frequency ){
+ this->frequency = frequency;
+ LPC_TIM0->MR0 = int(floor((SystemCoreClock/4)/frequency)); // SystemCoreClock/4Â = Timer increments in a second
+ if( LPC_TIM0->TC > LPC_TIM0->MR0 ){
+ LPC_TIM0->TCR = 3; // Reset
+ LPC_TIM0->TCR = 1; // Reset
+ }
+}
+
+void StepTicker::set_reset_delay( double seconds ){
+ LPC_TIM0->MR1 = int(floor(double(SystemCoreClock/4)*( seconds ))); // SystemCoreClock/4Â = Timer increments in a second
+}
+
+void StepTicker::tick(){
+ for (int i=0; i<this->hooks.size(); i++){
+ this->hooks.at(i)->call();
+ }
+}
+
+void StepTicker::reset_tick(){
+ for (int i=0; i<this->reset_hooks.size(); i++){
+ this->reset_hooks.at(i)->call();
+ }
+}
+
+extern "C" void TIMER0_IRQHandler (void){
+ if((LPC_TIM0->IR >> 0) & 1){ // If interrupt register set for MR0
+ LPC_TIM0->IR |= 1 << 0; // Reset it
+ global_step_ticker->tick();
+ }
+ if((LPC_TIM0->IR >> 1) & 1){ // If interrupt register set for MR1
+ LPC_TIM0->IR |= 1 << 1; // Reset it
+ global_step_ticker->reset_tick();
+ }
+}
+
+
diff -r 000000000000 -r 31e91bb0ef3c libs/StepTicker.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/StepTicker.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,58 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+
+#ifndef STEPTICKER_H
+#define STEPTICKER_H
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+
+
+class StepTicker{
+ public:
+ StepTicker();
+ void set_frequency( double frequency );
+ void tick();
+ void set_reset_delay( double seconds );
+ void reset_tick();
+
+ // For some reason this can't go in the .cpp, see : http://mbed.org/forum/mbed/topic/2774/?page=1#comment-14221
+ template<typename T> void attach( T *optr, uint32_t ( T::*fptr )( uint32_t ) ){
+ FPointer* hook = new FPointer();
+ hook->attach(optr, fptr);
+ this->hooks.push_back(hook);
+ }
+
+ template<typename T> void reset_attach( T *optr, uint32_t ( T::*fptr )( uint32_t ) ){
+ FPointer* reset_hook = new FPointer();
+ reset_hook->attach(optr, fptr);
+ this->reset_hooks.push_back(reset_hook);
+ }
+
+
+ vector<FPointer*> hooks;
+ vector<FPointer*> reset_hooks;
+ double frequency;
+
+};
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/StreamOutput.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/StreamOutput.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,22 @@
+#ifndef STREAMOUTPUT_H
+#define STREAMOUTPUT_H
+
+class StreamOutput {
+ public:
+ StreamOutput(){}
+ virtual int printf(const char* format, ...){return 0;}
+
+
+
+
+};
+
+
+
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c libs/nuts_bolts.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/nuts_bolts.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,43 @@ +/* +nuts_bolts.h - cartesian robot controller. +Part of Grbl + +Copyright (c) 2009-2011 Simen Svale Skogsrud + +Grbl 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. + +Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef nuts_bolts_h +#define nuts_bolts_h + +#include <string> +using std::string; + +#define X_AXIS 0 +#define Y_AXIS 1 +#define Z_AXIS 2 + +#define ALPHA_STEPPER 0 +#define BETA_STEPPER 1 +#define GAMMA_STEPPER 2 + +#define clear_vector(a) memset(a, 0, sizeof(a)) +#define clear_vector_double(a) memset(a, 0.0, sizeof(a)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +#define dd(...) LPC_GPIO2->FIODIR = 0xffff; LPC_GPIO2->FIOCLR = 0xffff; LPC_GPIO2->FIOSET = __VA_ARGS__ + + +#endif +
diff -r 000000000000 -r 31e91bb0ef3c libs/utils.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libs/utils.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,95 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Kernel.h"
+#include "libs/utils.h"
+#include "system_LPC17xx.h"
+using namespace std;
+#include <string>
+using std::string;
+#include <cstring>
+
+
+uint16_t get_checksum(string to_check){
+ // From:Â http://en.wikipedia.org/wiki/Fletcher%27s_checksum
+ uint16_t sum1 = 0;
+ uint16_t sum2 = 0;
+ for( int index = 0; index < to_check.length(); ++index ){
+ sum1 = (sum1 + to_check[index]) % 255;
+ sum2 = (sum2 + sum1) % 255;
+ }
+ return (sum2 << 8) | sum1;
+}
+
+vector<uint16_t> get_checksums(string key){
+ key = key.append(" ");
+ vector<uint16_t> check_sums;
+ size_t begin_key = 0;
+ while( begin_key < key.size()-1 ){
+ size_t end_key = key.find_first_of(" .", begin_key);
+ string key_node = key.substr(begin_key, end_key - begin_key);
+ check_sums.push_back(get_checksum(key_node));
+ begin_key = end_key + 1;
+ }
+ return check_sums;
+}
+
+// Convert to lowercase
+string lc(string str){
+ for (int i=0;i<strlen(str.c_str());i++)
+ if (str[i] >= 0x41 && str[i] <= 0x5A)
+ str[i] = str[i] + 0x20;
+ return str;
+}
+
+// Remove non-number characters
+string remove_non_number( string str ){
+ string number_mask = "0123456789-.";
+ size_t found=str.find_first_not_of(number_mask);
+ while (found!=string::npos){
+ //str[found]='*';
+ str.replace(found,1,"");
+ found=str.find_first_not_of(number_mask);
+ }
+ return str;
+}
+
+// Get the first parameter, and remove it from the original string
+string shift_parameter( string ¶meters ){
+ size_t beginning = parameters.find_first_of(" ");
+ if( beginning == string::npos ){ string temp = parameters; parameters = ""; return temp; }
+ string temp = parameters.substr( 0, beginning );
+ parameters = parameters.substr(beginning+1, parameters.size());
+ return temp;
+}
+
+// Separate command from arguments
+string get_arguments( string possible_command ){
+ size_t beginning = possible_command.find_first_of(" ");
+ if( beginning == string::npos ){ return ""; }
+ return possible_command.substr( beginning+1, possible_command.size() - beginning);
+}
+
+// Returns true if the file exists
+bool file_exists( string file_name ){
+ bool exists = false;
+ FILE *lp = fopen(file_name.c_str(), "r");
+ if(lp){ exists = true; }
+ fclose(lp);
+ return exists;
+}
+
+// Prepares and executes a watchdog reset
+void system_reset( void ){
+ LPC_WDT->WDCLKSEL = 0x1; // Set CLK src to PCLK
+ uint32_t clk = SystemCoreClock / 16; // WD has a fixed /4 prescaler, PCLK default is /4
+ LPC_WDT->WDTC = 1 * (float)clk; // Reset in 1 second
+ LPC_WDT->WDMOD = 0x3; // Enabled and Reset
+ LPC_WDT->WDFEED = 0xAA; // Kick the dog!
+ LPC_WDT->WDFEED = 0x55;
+}
+
diff -r 000000000000 -r 31e91bb0ef3c libs/utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/utils.h Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,29 @@ +#ifndef utils_h +#define utils_h + +#include <stdint.h> +using namespace std; +#include <string> +#include <vector> +using std::string; + +string lc(string str); + +string remove_non_number( string str ); + +uint16_t get_checksum(string to_check); + +vector<uint16_t> get_checksums(string key); + +string shift_parameter( string ¶meters ); + +string get_arguments( string possible_command ); + +bool file_exists( string file_name ); + +void system_reset( void ); + + + + +#endif
diff -r 000000000000 -r 31e91bb0ef3c main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,54 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Kernel.h"
+#include "modules/tools/laser/Laser.h"
+#include "modules/tools/spindle/Spindle.h"
+#include "modules/tools/extruder/Extruder.h"
+#include "modules/tools/temperaturecontrol/TemperatureControlPool.h"
+#include "modules/robot/Player.h"
+#include "modules/utils/simpleshell/SimpleShell.h"
+#include "modules/utils/configurator/Configurator.h"
+#include "modules/utils/currentcontrol/CurrentControl.h"
+#include "modules/utils/pausebutton/PauseButton.h"
+//#include "libs/ChaNFSSD/SDFileSystem.h"
+#include "libs/Config.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+
+//#include "libs/USBCDCMSC/USBCDCMSC.h"
+LocalFileSystem local("local");
+//SDFileSystem sd(p5, p6, p7, p8, "sd"); // LPC17xx specific : comment if you are not using a SD�card ( for example with a mBed ).
+//LocalFileSystem local("local"); // LPC17xx specific :�comment if you are not running a mBed
+//USBCDCMSC cdcmsc(&sd); // LPC17xx specific :�Composite serial + msc USB device
+
+int main() {
+
+ Kernel* kernel = new Kernel();
+
+ kernel->serial->printf("Smoothie ( grbl port ) version 0.6.1_mbed \r\n");
+
+
+
+ //kernel->add_module( new Laser(p21) );
+ //kernel->add_module( new Extruder() );
+ kernel->add_module( new Spindle() );
+ kernel->add_module( new SimpleShell() );
+ kernel->add_module( new Configurator() );
+ kernel->add_module( new CurrentControl() );
+ kernel->add_module( new TemperatureControlPool() );
+ kernel->add_module( new PauseButton() );
+
+ //kernel->add_module( &cdcmsc );
+
+ kernel->serial->printf("start\r\n");
+
+ while(1){
+ kernel->call_event(ON_MAIN_LOOP);
+ kernel->call_event(ON_IDLE);
+ }
+}
diff -r 000000000000 -r 31e91bb0ef3c mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Jul 31 21:11:18 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/10b9abbe79a6 \ No newline at end of file
diff -r 000000000000 -r 31e91bb0ef3c modules/communication/GcodeDispatch.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/communication/GcodeDispatch.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,115 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string>
+using std::string;
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "utils/Gcode.h"
+#include "libs/nuts_bolts.h"
+#include "GcodeDispatch.h"
+#include "modules/robot/Player.h"
+#include "libs/SerialMessage.h"
+#include "libs/StreamOutput.h"
+
+GcodeDispatch::GcodeDispatch(){}
+
+// Called when the module has just been loaded
+void GcodeDispatch::on_module_loaded() {
+ this->register_for_event(ON_CONSOLE_LINE_RECEIVED);
+ currentline = -1;
+}
+
+// When a command is received, if it is a Gcode, dispatch it as an object via an event
+void GcodeDispatch::on_console_line_received(void * line){
+ SerialMessage new_message = *static_cast<SerialMessage*>(line);
+ string possible_command = new_message.message;
+
+ char first_char = possible_command[0];
+ int ln = 0;
+ int cs = 0;
+ if( first_char == 'G' || first_char == 'M' || first_char == 'T' || first_char == 'N' ){
+
+ //Get linenumber
+ if( first_char == 'N' ){
+ Gcode full_line = Gcode();
+ full_line.command = possible_command;
+ full_line.stream = new_message.stream;
+ ln = (int) full_line.get_value('N');
+ int chksum = (int) full_line.get_value('*');
+
+ //Catch message if it is M110: Set Current Line Number
+ if( full_line.has_letter('M') ){
+ if( ((int) full_line.get_value('M')) == 110 ){
+ currentline = ln;
+ new_message.stream->printf("ok\r\n");
+ return;
+ }
+ }
+
+ //Strip checksum value from possible_command
+ size_t chkpos = possible_command.find_first_of("*");
+ possible_command = possible_command.substr(0, chkpos);
+ //Calculate checksum
+ if( chkpos != string::npos ){
+ for(int i = 0; possible_command[i] != '*' && possible_command[i] != NULL; i++)
+ cs = cs ^ possible_command[i];
+ cs &= 0xff; // Defensive programming...
+ cs -= chksum;
+ }
+ //Strip line number value from possible_command
+ size_t lnsize = possible_command.find_first_of(" ") + 1;
+ possible_command = possible_command.substr(lnsize);
+
+ }else{
+ //Assume checks succeeded
+ cs = 0x00;
+ ln = currentline + 1;
+ }
+
+ //Remove comments
+ size_t comment = possible_command.find_first_of(";");
+ if( comment != string::npos ){ possible_command = possible_command.substr(0, comment); }
+
+ //If checksum passes then process message, else request resend
+ int nextline = currentline + 1;
+ if( cs == 0x00 && ln == nextline ){
+ if( first_char == 'N' ) {
+ currentline = nextline;
+ }
+
+ while(possible_command.size() > 0) {
+ size_t nextcmd = possible_command.find_first_of("GMT", possible_command.find_first_of("GMT")+1);
+ string single_command;
+ if(nextcmd == string::npos) {
+ single_command = possible_command;
+ possible_command = "";
+ }
+ else {
+ single_command = possible_command.substr(0,nextcmd);
+ possible_command = possible_command.substr(nextcmd);
+ }
+ //Prepare gcode for dispatch
+ Gcode gcode = Gcode();
+ gcode.command = single_command;
+ gcode.stream = new_message.stream;
+
+ //Dispatch message!
+ this->kernel->call_event(ON_GCODE_RECEIVED, &gcode );
+ new_message.stream->printf("ok\r\n");
+ }
+ }else{
+ //Request resend
+ new_message.stream->printf("rs N%d\r\n", nextline);
+ }
+
+ // Ignore comments and blank lines
+ }else if( first_char == ';' || first_char == '(' || first_char == ' ' || first_char == '\n' || first_char == '\r' ){
+ new_message.stream->printf("ok\r\n");
+ }
+}
+
diff -r 000000000000 -r 31e91bb0ef3c modules/communication/GcodeDispatch.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/communication/GcodeDispatch.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,29 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GCODE_DISPATCH_H
+#define GCODE_DISPATCH_H
+
+#include <string>
+using std::string;
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "utils/Gcode.h"
+
+#include "libs/StreamOutput.h"
+
+class GcodeDispatch : public Module {
+ public:
+ GcodeDispatch();
+
+ virtual void on_module_loaded();
+ virtual void on_console_line_received(void* line);
+ private:
+ int currentline;
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/communication/SerialConsole.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/communication/SerialConsole.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,88 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string>
+#include <stdarg.h>
+using std::string;
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "SerialConsole.h"
+#include "libs/RingBuffer.h"
+#include "libs/SerialMessage.h"
+#include "libs/StreamOutput.h"
+
+// Serial reading module
+// Treats every received line as a command and passes it ( via event call ) to the command dispatcher.
+// The command dispatcher will then ask other modules if they can do something with it
+SerialConsole::SerialConsole( PinName rx_pin, PinName tx_pin, int baud_rate ){
+ this->serial = new mbed::Serial( rx_pin, tx_pin );
+ this->serial->printf("T1\n");
+ this->serial->baud(baud_rate);
+}
+
+// Called when the module has just been loaded
+void SerialConsole::on_module_loaded() {
+ // We want to be called every time a new char is received
+ this->serial->attach(this, &SerialConsole::on_serial_char_received, mbed::Serial::RxIrq);
+
+ // We only call the command dispatcher in the main loop, nowhere else
+ this->register_for_event(ON_MAIN_LOOP);
+}
+
+// Called on Serial::RxIrq interrupt, meaning we have received a char
+void SerialConsole::on_serial_char_received(){
+ while(this->serial->readable()){
+ char received = this->serial->getc();
+ // convert CR to NL (for host OSs that don't send NL)
+ if( received == '\r' ){ received = '\n'; }
+ this->buffer.push_back(received);
+ }
+}
+
+// Actual event calling must happen in the main loop because if it happens in the interrupt we will loose data
+void SerialConsole::on_main_loop(void * argument){
+ if( this->has_char('\n') ){
+ int index = 0;
+ string received;
+ while(1){
+ char c;
+ this->buffer.pop_front(c);
+ if( c == '\n' ){
+ struct SerialMessage message;
+ message.message = received;
+ message.stream = this;
+ this->kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
+ return;
+ }else{
+ received += c;
+ }
+ }
+ }
+}
+
+
+int SerialConsole::printf(const char* format, ...){
+ va_list args;
+ int result=0;
+ va_start (args, format);
+ this->serial->printf( format, args);
+ va_end (args);
+ return result;
+}
+
+
+bool SerialConsole::has_char(char letter){
+ int index = this->buffer.head;
+ while( index != this->buffer.tail ){
+ if( this->buffer.buffer[index] == letter ){
+ return true;
+ }
+ index = this->buffer.next_block_index(index);
+ }
+ return false;
+}
diff -r 000000000000 -r 31e91bb0ef3c modules/communication/SerialConsole.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/communication/SerialConsole.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,39 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SERIALCONSOLE_H
+#define SERIALCONSOLE_H
+
+#include "libs/Module.h"
+#include "mbed.h"
+#include "libs/Kernel.h"
+#include <vector>
+#include <string>
+using std::string;
+#include "libs/RingBuffer.h"
+#include "libs/StreamOutput.h"
+
+
+#define baud_rate_setting_checksum 10922
+
+class SerialConsole : public Module, public StreamOutput {
+ public:
+ SerialConsole( PinName rx_pin, PinName tx_pin, int baud_rate );
+
+ virtual void on_module_loaded();
+ void on_serial_char_received();
+ virtual void on_main_loop(void * argument);
+ bool has_char(char letter);
+ virtual int printf(const char* format, ...);
+
+ //string receive_buffer; // Received chars are stored here until a newline character is received
+ //vector<std::string> received_lines; // Received lines are stored here until they are requested
+ RingBuffer<char,256> buffer; // Receive buffer
+ mbed::Serial* serial;
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/communication/utils/Gcode.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/communication/utils/Gcode.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,53 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <string>
+using std::string;
+#include "Gcode.h"
+#include "libs/StreamOutput.h"
+
+#include <stdlib.h>
+
+
+Gcode::Gcode(){}
+
+// Whether or not a Gcode has a letter
+bool Gcode::has_letter( char letter ){
+ //return ( this->command->find( letter ) != string::npos );
+ for (size_t i=0; i < this->command.length(); i++){
+ if( this->command.at(i) == letter ){
+ return true;
+ }
+ }
+ return false;
+}
+
+// Retrieve the value for a given letter
+// We don't use the high-level methods of std::string because they call malloc and it's very bad to do that inside of interrupts
+double Gcode::get_value( char letter ){
+ //__disable_irq();
+ for (size_t i=0; i <= this->command.length()-1; i++){
+ if( letter == this->command.at(i) ){
+ size_t beginning = i+1;
+ char buffer[20];
+ for(size_t j=beginning; j <= this->command.length(); j++){
+ char c;
+ if( j == this->command.length() ){ c = ';'; }else{ c = this->command.at(j); }
+ if( c != '.' && c != '-' && ( c < '0' || c > '9' ) ){
+ buffer[j-beginning] = '\0';
+ //__enable_irq();
+ return atof(buffer);
+ }else{
+ buffer[j-beginning] = c;
+ }
+ }
+ }
+ }
+ //__enable_irq();
+ return 0;
+}
\ No newline at end of file
diff -r 000000000000 -r 31e91bb0ef3c modules/communication/utils/Gcode.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/communication/utils/Gcode.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,30 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef GCODE_H
+#define GCODE_H
+#include <string>
+using std::string;
+#include "libs/StreamOutput.h"
+// Object to represent a Gcode comman
+#include <stdlib.h>
+
+class Gcode {
+ public:
+ Gcode();
+ bool has_letter( char letter );
+ double get_value ( char letter );
+
+ string command;
+ double millimeters_of_travel;
+ bool call_on_gcode_execute_event_immediatly;
+ bool on_gcode_execute_event_called;
+
+ StreamOutput* stream;
+};
+#endif
\ No newline at end of file
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Block.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Block.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,221 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include <math.h>
+#include <string>
+#include "Block.h"
+#include "Planner.h"
+#include "Player.h"
+using std::string;
+#include <vector>
+#include "../communication/utils/Gcode.h"
+
+Block::Block(){
+ clear_vector(this->steps);
+ this->times_taken = 0; // A block can be "taken" by any number of modules, and the next block is not moved to until all the modules have "released" it. This value serves as a tracker.
+ this->is_ready = false;
+ this->initial_rate = -1;
+ this->final_rate = -1;
+}
+
+void Block::debug(Kernel* kernel){
+ kernel->serial->printf("%p: steps:%4d|%4d|%4d(max:%4d) nominal:r%10d/s%6.1f mm:%9.6f rdelta:%8d acc:%5d dec:%5d rates:%10d>%10d taken:%d ready:%d \r\n", this, this->steps[0], this->steps[1], this->steps[2], this->steps_event_count, this->nominal_rate, this->nominal_speed, this->millimeters, this->rate_delta, this->accelerate_until, this->decelerate_after, this->initial_rate, this->final_rate, this->times_taken, this->is_ready );
+}
+
+
+// Calculate a braking factor to reach baseline speed which is max_jerk/2, e.g. the
+// speed under which you cannot exceed max_jerk no matter what you do.
+double Block::compute_factor_for_safe_speed(){
+ return( this->planner->max_jerk / this->nominal_speed );
+}
+
+
+// Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors.
+// The factors represent a factor of braking and must be in the range 0.0-1.0.
+// +--------+ <- nominal_rate
+// / \
+// nominal_rate*entry_factor -> + \
+// | + <- nominal_rate*exit_factor
+// +-------------+
+// time -->
+void Block::calculate_trapezoid( double entryfactor, double exitfactor ){
+
+ this->initial_rate = ceil(this->nominal_rate * entryfactor); // (step/min)
+ this->final_rate = ceil(this->nominal_rate * exitfactor); // (step/min)
+ double acceleration_per_minute = this->rate_delta * this->planner->kernel->stepper->acceleration_ticks_per_second * 60.0;
+ int accelerate_steps = ceil( this->estimate_acceleration_distance( this->initial_rate, this->nominal_rate, acceleration_per_minute ) );
+ int decelerate_steps = ceil( this->estimate_acceleration_distance( this->nominal_rate, this->final_rate, -acceleration_per_minute ) );
+
+ // Calculate the size of Plateau of Nominal Rate.
+ int plateau_steps = this->steps_event_count-accelerate_steps-decelerate_steps;
+
+ // Is the Plateau of Nominal Rate smaller than nothing? That means no cruising, and we will
+ // have to use intersection_distance() to calculate when to abort acceleration and start braking
+ // in order to reach the final_rate exactly at the end of this block.
+ if (plateau_steps < 0) {
+ accelerate_steps = ceil(this->intersection_distance(this->initial_rate, this->final_rate, acceleration_per_minute, this->steps_event_count));
+ accelerate_steps = max( accelerate_steps, 0 ); // Check limits due to numerical round-off
+ accelerate_steps = min( accelerate_steps, int(this->steps_event_count) );
+ plateau_steps = 0;
+ }
+
+ this->accelerate_until = accelerate_steps;
+ this->decelerate_after = accelerate_steps+plateau_steps;
+
+ // TODO: FIX THIS: DIRTY HACK so that we don't end too early for blocks with 0 as final_rate. Doing the math right would be better. Probably fixed in latest grbl
+ if( this->final_rate < 0.01 ){
+ this->decelerate_after += ( this->nominal_rate / 60 / this->planner->kernel->stepper->acceleration_ticks_per_second ) * 3;
+ }
+
+}
+
+// Calculates the distance (not time) it takes to accelerate from initial_rate to target_rate using the
+// given acceleration:
+double Block::estimate_acceleration_distance(double initialrate, double targetrate, double acceleration) {
+ return( (targetrate*targetrate-initialrate*initialrate)/(2L*acceleration));
+}
+
+// This function gives you the point at which you must start braking (at the rate of -acceleration) if
+// you started at speed initial_rate and accelerated until this point and want to end at the final_rate after
+// a total travel of distance. This can be used to compute the intersection point between acceleration and
+// deceleration in the cases where the trapezoid has no plateau (i.e. never reaches maximum speed)
+//
+/* + <- some maximum rate we don't care about
+ /|\
+ / | \
+ / | + <- final_rate
+ / | |
+ initial_rate -> +----+--+
+ ^ ^
+ | |
+ intersection_distance distance */
+double Block::intersection_distance(double initialrate, double finalrate, double acceleration, double distance) {
+ return((2*acceleration*distance-initialrate*initialrate+finalrate*finalrate)/(4*acceleration));
+}
+
+// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the
+// acceleration within the allotted distance.
+inline double max_allowable_speed(double acceleration, double target_velocity, double distance) {
+ return(
+ sqrt(target_velocity*target_velocity-2L*acceleration*60*60*distance) //Was acceleration*60*60*distance, in case this breaks, but here we prefer to use seconds instead of minutes
+ );
+}
+
+
+// Called by Planner::recalculate() when scanning the plan from last to first entry.
+void Block::reverse_pass(Block* next, Block* previous){
+
+ if (next) {
+ // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
+ // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
+ // check for maximum allowable speed reductions to ensure maximum possible planned speed.
+ if (this->entry_speed != this->max_entry_speed) {
+
+ // If nominal length true, max junction speed is guaranteed to be reached. Only compute
+ // for max allowable speed if block is decelerating and nominal length is false.
+ if ((!this->nominal_length_flag) && (this->max_entry_speed > next->entry_speed)) {
+ this->entry_speed = min( this->max_entry_speed, max_allowable_speed(-this->planner->acceleration,next->entry_speed,this->millimeters));
+ } else {
+ this->entry_speed = this->max_entry_speed;
+ }
+ this->recalculate_flag = true;
+
+ }
+ } // Skip last block. Already initialized and set for recalculation.
+
+}
+
+
+// Called by Planner::recalculate() when scanning the plan from first to last entry.
+void Block::forward_pass(Block* previous, Block* next){
+
+ if(!previous) { return; } // Begin planning after buffer_tail
+
+ // If the previous block is an acceleration block, but it is not long enough to complete the
+ // full speed change within the block, we need to adjust the entry speed accordingly. Entry
+ // speeds have already been reset, maximized, and reverse planned by reverse planner.
+ // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.
+ if (!previous->nominal_length_flag) {
+ if (previous->entry_speed < this->entry_speed) {
+ double entry_speed = min( this->entry_speed,
+ max_allowable_speed(-this->planner->acceleration,previous->entry_speed,previous->millimeters) );
+
+ // Check for junction speed change
+ if (this->entry_speed != entry_speed) {
+ this->entry_speed = entry_speed;
+ this->recalculate_flag = true;
+ }
+ }
+ }
+
+}
+
+
+// Gcodes are attached to their respective blocks so that on_gcode_execute can be called with it
+void Block::append_gcode(Gcode* gcode){
+ __disable_irq();
+ this->gcodes.push_back(*gcode);
+ __enable_irq();
+}
+
+// The attached gcodes are then poped and the on_gcode_execute event is called with them as a parameter
+void Block::pop_and_execute_gcode(Kernel* &kernel){
+ Block* block = const_cast<Block*>(this);
+ for(unsigned short index=0; index<block->gcodes.size(); index++){
+ kernel->call_event(ON_GCODE_EXECUTE, &(block->gcodes[index]));
+ }
+}
+
+// Signal the player that this block is ready to be injected into the system
+void Block::ready(){
+ this->is_ready = true;
+ this->player->new_block_added();
+}
+
+// Mark the block as taken by one more module
+void Block::take(){
+ this->times_taken++;
+}
+
+// Mark the block as no longer taken by one module, go to next block if this free's it
+void Block::release(){
+ this->times_taken--;
+ if( this->times_taken < 1 ){
+ this->player->kernel->call_event(ON_BLOCK_END, this);
+ this->pop_and_execute_gcode(this->player->kernel);
+ Player* player = this->player;
+
+ if( player->queue.size() > 0 ){
+ player->queue.delete_first();
+ }
+
+ if( player->looking_for_new_block == false ){
+ if( player->queue.size() > 0 ){
+ Block* candidate = player->queue.get_ref(0);
+ if( candidate->is_ready ){
+ player->current_block = candidate;
+ player->kernel->call_event(ON_BLOCK_BEGIN, player->current_block);
+ if( player->current_block->times_taken < 1 ){
+ player->current_block->release();
+ }
+ }else{
+
+ player->current_block = NULL;
+
+ }
+ }else{
+ player->current_block = NULL;
+ }
+ }
+ }
+}
+
+
+
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Block.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Block.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,81 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef BLOCK_H
+#define BLOCK_H
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+using namespace std;
+#include <string>
+#include <vector>
+
+#include "../communication/utils/Gcode.h"
+#include "Planner.h"
+class Planner;
+class Player;
+
+double max_allowable_speed( double acceleration, double target_velocity, double distance);
+
+class Block {
+ public:
+ Block();
+ double compute_factor_for_safe_speed();
+ void calculate_trapezoid( double entry_factor, double exit_factor );
+ double estimate_acceleration_distance( double initial_rate, double target_rate, double acceleration );
+ double intersection_distance(double initial_rate, double final_rate, double acceleration, double distance);
+ void reverse_pass(Block* previous, Block* next);
+ void forward_pass(Block* previous, Block* next);
+ void debug(Kernel* kernel);
+ void append_gcode(Gcode* gcode);
+ void pop_and_execute_gcode(Kernel* &kernel);
+ double get_duration_left(unsigned int already_taken_steps);
+ void take();
+ void release();
+ void ready();
+
+ vector<std::string> commands;
+ vector<double> travel_distances;
+ vector<Gcode> gcodes;
+
+ unsigned int steps[3]; // Number of steps for each axis for this block
+ unsigned int steps_event_count; // Steps for the longest axis
+ unsigned int nominal_rate; // Nominal rate in steps per minute
+ float nominal_speed; // Nominal speed in mm per minute
+ float millimeters; // Distance for this move
+ double entry_speed;
+ unsigned int rate_delta; // Nomber of steps to add to the speed for each acceleration tick
+ int initial_rate; // Initial speed in steps per minute
+ int final_rate; // Final speed in steps per minute
+ unsigned int accelerate_until; // Stop accelerating after this number of steps
+ unsigned int decelerate_after; // Start decelerating after this number of steps
+ unsigned int direction_bits; // Direction for each axis in bit form, relative to the direction port's mask
+
+
+ uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction
+ uint8_t nominal_length_flag; // Planner flag for nominal speed always reached
+
+ double max_entry_speed;
+ Planner* planner;
+ Player* player;
+
+ bool is_ready;
+
+ short times_taken; // A block can be "taken" by any number of modules, and the next block is not moved to until all the modules have "released" it. This value serves as a tracker.
+
+};
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Planner.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Planner.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,264 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl)
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/RingBuffer.h"
+#include "../communication/utils/Gcode.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "Block.h"
+#include "Planner.h"
+#include "Player.h"
+
+
+Planner::Planner(){
+ clear_vector(this->position);
+ clear_vector_double(this->previous_unit_vec);
+ this->previous_nominal_speed = 0.0;
+ this->has_deleted_block = false;
+}
+
+void Planner::on_module_loaded(){
+ this->on_config_reload(this);
+}
+
+void Planner::on_config_reload(void* argument){
+ this->acceleration = this->kernel->config->value(acceleration_checksum )->by_default(100 )->as_number();
+ this->max_jerk = this->kernel->config->value(max_jerk_checksum )->by_default(100 )->as_number();
+ this->junction_deviation = this->kernel->config->value(junction_deviation_checksum )->by_default(0.05)->as_number();
+}
+
+
+// Append a block to the queue, compute it's speed factors
+void Planner::append_block( int target[], double feed_rate, double distance, double deltas[] ){
+
+ // Stall here if the queue is ful
+ this->kernel->player->wait_for_queue(2);
+
+ Block* block = this->kernel->player->new_block();
+ block->planner = this;
+
+ // Direction bits
+ block->direction_bits = 0;
+ for( int stepper=ALPHA_STEPPER; stepper<=GAMMA_STEPPER; stepper++){
+ if( target[stepper] < position[stepper] ){ block->direction_bits |= (1<<stepper); }
+ }
+
+ // Number of steps for each stepper
+ for( int stepper=ALPHA_STEPPER; stepper<=GAMMA_STEPPER; stepper++){ block->steps[stepper] = labs(target[stepper] - this->position[stepper]); }
+
+ // Max number of steps, for all axes
+ block->steps_event_count = max( block->steps[ALPHA_STEPPER], max( block->steps[BETA_STEPPER], block->steps[GAMMA_STEPPER] ) );
+ //if( block->steps_event_count == 0 ){ this->computing = false; return; }
+
+ block->millimeters = distance;
+ double inverse_millimeters = 0;
+ if( distance > 0 ){ inverse_millimeters = 1.0/distance; }
+
+ // Calculate speed in mm/minute for each axis. No divide by zero due to previous checks.
+ // NOTE: Minimum stepper speed is limited by MINIMUM_STEPS_PER_MINUTE in stepper.c
+ double inverse_minute = feed_rate * inverse_millimeters;
+ if( distance > 0 ){
+ block->nominal_speed = block->millimeters * inverse_minute; // (mm/min) Always > 0
+ block->nominal_rate = ceil(block->steps_event_count * inverse_minute); // (step/min) Always > 0
+ }else{
+ block->nominal_speed = 0;
+ block->nominal_rate = 0;
+ }
+
+ //this->kernel->serial->printf("nom_speed: %f nom_rate: %u step_event_count: %u block->steps_z: %u \r\n", block->nominal_speed, block->nominal_rate, block->steps_event_count, block->steps[2] );
+
+ // Compute the acceleration rate for the trapezoid generator. Depending on the slope of the line
+ // average travel per step event changes. For a line along one axis the travel per step event
+ // is equal to the travel/step in the particular axis. For a 45 degree line the steppers of both
+ // axes might step for every step event. Travel per step event is then sqrt(travel_x^2+travel_y^2).
+ // To generate trapezoids with contant acceleration between blocks the rate_delta must be computed
+ // specifically for each line to compensate for this phenomenon:
+ // Convert universal acceleration for direction-dependent stepper rate change parameter
+ block->rate_delta = ceil( block->steps_event_count*inverse_millimeters * this->acceleration*60.0 / this->kernel->stepper->acceleration_ticks_per_second ); // (step/min/acceleration_tick)
+
+ // Compute path unit vector
+ double unit_vec[3];
+ unit_vec[X_AXIS] = deltas[X_AXIS]*inverse_millimeters;
+ unit_vec[Y_AXIS] = deltas[Y_AXIS]*inverse_millimeters;
+ unit_vec[Z_AXIS] = deltas[Z_AXIS]*inverse_millimeters;
+
+ // Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
+ // Let a circle be tangent to both previous and current path line segments, where the junction
+ // deviation is defined as the distance from the junction to the closest edge of the circle,
+ // colinear with the circle center. The circular segment joining the two paths represents the
+ // path of centripetal acceleration. Solve for max velocity based on max acceleration about the
+ // radius of the circle, defined indirectly by junction deviation. This may be also viewed as
+ // path width or max_jerk in the previous grbl version. This approach does not actually deviate
+ // from path, but used as a robust way to compute cornering speeds, as it takes into account the
+ // nonlinearities of both the junction angle and junction velocity.
+ double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed
+
+ if (this->kernel->player->queue.size() > 1 && (this->previous_nominal_speed > 0.0)) {
+ // Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
+ // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
+ double cos_theta = - this->previous_unit_vec[X_AXIS] * unit_vec[X_AXIS]
+ - this->previous_unit_vec[Y_AXIS] * unit_vec[Y_AXIS]
+ - this->previous_unit_vec[Z_AXIS] * unit_vec[Z_AXIS] ;
+
+ // Skip and use default max junction speed for 0 degree acute junction.
+ if (cos_theta < 0.95) {
+ vmax_junction = min(this->previous_nominal_speed,block->nominal_speed);
+ // Skip and avoid divide by zero for straight junctions at 180 degrees. Limit to min() of nominal speeds.
+ if (cos_theta > -0.95) {
+ // Compute maximum junction velocity based on maximum acceleration and junction deviation
+ double sin_theta_d2 = sqrt(0.5*(1.0-cos_theta)); // Trig half angle identity. Always positive.
+ vmax_junction = min(vmax_junction,
+ sqrt(this->acceleration*60*60 * this->junction_deviation * sin_theta_d2/(1.0-sin_theta_d2)) );
+ }
+ }
+ }
+ block->max_entry_speed = vmax_junction;
+
+ // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED.
+ double v_allowable = this->max_allowable_speed(-this->acceleration,0.0,block->millimeters); //TODO:Â Get from config
+ block->entry_speed = min(vmax_junction, v_allowable);
+
+ // Initialize planner efficiency flags
+ // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.
+ // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then
+ // the current block and next block junction speeds are guaranteed to always be at their maximum
+ // junction speeds in deceleration and acceleration, respectively. This is due to how the current
+ // block nominal speed limits both the current and next maximum junction speeds. Hence, in both
+ // the reverse and forward planners, the corresponding block junction speed will always be at the
+ // the maximum junction speed and may always be ignored for any speed reduction checks.
+ if (block->nominal_speed <= v_allowable) { block->nominal_length_flag = true; }
+ else { block->nominal_length_flag = false; }
+ block->recalculate_flag = true; // Always calculate trapezoid for new block
+
+ // Update previous path unit_vector and nominal speed
+ memcpy(this->previous_unit_vec, unit_vec, sizeof(unit_vec)); // previous_unit_vec[] = unit_vec[]
+ this->previous_nominal_speed = block->nominal_speed;
+
+ // Update current position
+ memcpy(this->position, target, sizeof(int)*3);
+
+ // Math-heavy re-computing of the whole queue to take the new
+ this->recalculate();
+
+ // The block can now be used
+ block->ready();
+
+}
+
+
+// Recalculates the motion plan according to the following algorithm:
+//
+// 1. Go over every block in reverse order and calculate a junction speed reduction (i.e. block_t.entry_factor)
+// so that:
+// a. The junction jerk is within the set limit
+// b. No speed reduction within one block requires faster deceleration than the one, true constant
+// acceleration.
+// 2. Go over every block in chronological order and dial down junction speed reduction values if
+// a. The speed increase within one block would require faster accelleration than the one, true
+// constant acceleration.
+//
+// When these stages are complete all blocks have an entry_factor that will allow all speed changes to
+// be performed using only the one, true constant acceleration, and where no junction jerk is jerkier than
+// the set limit. Finally it will:
+//
+// 3. Recalculate trapezoids for all blocks.
+//
+void Planner::recalculate() {
+ //this->kernel->serial->printf("recalculate last: %p, queue size: %d \r\n", this->kernel->player->queue.get_ref( this->kernel->player->queue.size()-1 ), this->kernel->player->queue.size() );
+ this->reverse_pass();
+ this->forward_pass();
+ this->recalculate_trapezoids();
+}
+
+// Planner::recalculate() needs to go over the current plan twice. Once in reverse and once forward. This
+// implements the reverse pass.
+void Planner::reverse_pass(){
+ // For each block
+ int block_index = this->kernel->player->queue.tail;
+ Block* blocks[3] = {NULL,NULL,NULL};
+
+ while(block_index!=this->kernel->player->queue.head){
+ block_index = this->kernel->player->queue.prev_block_index( block_index );
+ blocks[2] = blocks[1];
+ blocks[1] = blocks[0];
+ blocks[0] = &this->kernel->player->queue.buffer[block_index];
+ if( blocks[1] == NULL ){ continue; }
+ blocks[1]->reverse_pass(blocks[2], blocks[0]);
+ }
+
+}
+
+// Planner::recalculate() needs to go over the current plan twice. Once in reverse and once forward. This
+// implements the forward pass.
+void Planner::forward_pass() {
+ // For each block
+ int block_index = this->kernel->player->queue.head;
+ Block* blocks[3] = {NULL,NULL,NULL};
+
+ while(block_index!=this->kernel->player->queue.tail){
+ blocks[0] = blocks[1];
+ blocks[1] = blocks[2];
+ blocks[2] = &this->kernel->player->queue.buffer[block_index];
+ if( blocks[0] == NULL ){ continue; }
+ blocks[1]->forward_pass(blocks[0],blocks[2]);
+ block_index = this->kernel->player->queue.next_block_index( block_index );
+ }
+ blocks[2]->forward_pass(blocks[1],NULL);
+
+}
+
+// Recalculates the trapezoid speed profiles for flagged blocks in the plan according to the
+// entry_speed for each junction and the entry_speed of the next junction. Must be called by
+// planner_recalculate() after updating the blocks. Any recalulate flagged junction will
+// compute the two adjacent trapezoids to the junction, since the junction speed corresponds
+// to exit speed and entry speed of one another.
+void Planner::recalculate_trapezoids() {
+ int block_index = this->kernel->player->queue.head;
+ Block* current;
+ Block* next = NULL;
+
+ while(block_index != this->kernel->player->queue.tail){
+ current = next;
+ next = &this->kernel->player->queue.buffer[block_index];
+ //this->kernel->serial->printf("index:%d current:%p next:%p \r\n", block_index, current, next );
+ if( current ){
+ // Recalculate if current block entry or exit junction speed has changed.
+ if( current->recalculate_flag || next->recalculate_flag ){
+ current->calculate_trapezoid( current->entry_speed/current->nominal_speed, next->entry_speed/current->nominal_speed );
+ current->recalculate_flag = false;
+ }
+ }
+ block_index = this->kernel->player->queue.next_block_index( block_index );
+ }
+
+ // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
+ next->calculate_trapezoid( next->entry_speed/next->nominal_speed, MINIMUM_PLANNER_SPEED/next->nominal_speed); //TODO: Make configuration option
+ next->recalculate_flag = false;
+
+}
+
+// Debug function
+void Planner::dump_queue(){
+ for( int index = 0; index <= this->kernel->player->queue.size()-1; index++ ){
+ if( index > 10 && index < this->kernel->player->queue.size()-10 ){ continue; }
+ this->kernel->serial->printf("block %03d > ", index);
+ this->kernel->player->queue.get_ref(index)->debug(this->kernel);
+ }
+}
+
+// Calculates the maximum allowable speed at this point when you must be able to reach target_velocity using the
+// acceleration within the allotted distance.
+double Planner::max_allowable_speed(double acceleration, double target_velocity, double distance) {
+ return(
+ sqrt(target_velocity*target_velocity-2L*acceleration*60*60*distance) //Was acceleration*60*60*distance, in case this breaks, but here we prefer to use seconds instead of minutes
+ );
+}
+
+
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Planner.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Planner.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,54 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PLANNER_H
+#define PLANNER_H
+
+#include <vector>
+#include "libs/RingBuffer.h"
+#include "../communication/utils/Gcode.h"
+#include "Block.h"
+
+#define acceleration_checksum 25326
+#define max_jerk_checksum 61012
+#define junction_deviation_checksum 6035
+
+// TODO: Get from config
+#define MINIMUM_PLANNER_SPEED 0.0
+using namespace std;
+
+
+class Planner : public Module {
+ public:
+ Planner();
+ void append_block( int target[], double feed_rate, double distance, double deltas[] );
+ double max_allowable_speed( double acceleration, double target_velocity, double distance);
+ void recalculate();
+ void reverse_pass();
+ void forward_pass();
+ void recalculate_trapezoids();
+ void dump_queue();
+ Block* get_current_block();
+ void cleanup_queue();
+ virtual void on_module_loaded();
+ virtual void on_config_reload(void* argument);
+
+ int position[3]; // Current position, in steps
+ double previous_unit_vec[3];
+ Block last_deleted_block; // Item -1 in the queue, TODO: Grbl does not need this, but Smoothie won't work without it, we are probably doing something wrong
+ bool has_deleted_block; // Flag for above value
+ float previous_nominal_speed;
+
+ double acceleration; // Setting
+ double max_jerk; // Setting
+ double junction_deviation; // Setting
+
+};
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Player.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Player.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,99 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl)
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+using namespace std;
+#include <vector>
+#include "libs/nuts_bolts.h"
+#include "libs/RingBuffer.h"
+#include "../communication/utils/Gcode.h"
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "Timer.h" // mbed.h lib
+#include "wait_api.h" // mbed.h lib
+#include "Block.h"
+#include "Player.h"
+#include "Planner.h"
+
+Player::Player(){
+ this->current_block = NULL;
+ this->looking_for_new_block = false;
+}
+
+// Append a block to the list
+Block* Player::new_block(){
+
+ // Clean up the vector of commands in the block we are about to replace
+ // It is quite strange to do this here, we really should do it inside Block->pop_and_execute_gcode
+ // but that function is called inside an interrupt and thus can break everything if the interrupt was trigerred during a memory access
+ //Block* block = this->queue.get_ref( this->queue.size()-1 );
+ Block* block = this->queue.get_ref( this->queue.size() );
+ if( block->player == this ){
+ for(short index=0; index<block->gcodes.size(); index++){
+ block->gcodes.pop_back();
+ }
+ }
+
+ // Create a new virgin Block in the queue
+ this->queue.push_back(Block());
+ block = this->queue.get_ref( this->queue.size()-1 );
+ block->is_ready = false;
+ block->initial_rate = -2;
+ block->final_rate = -2;
+ block->player = this;
+
+ return block;
+}
+
+// Used by blocks to signal when they are ready to be used by the system
+void Player::new_block_added(){
+ if( this->current_block == NULL ){
+ this->pop_and_process_new_block(33);
+ }
+}
+
+// Process a new block in the queue
+void Player::pop_and_process_new_block(int debug){
+ if( this->looking_for_new_block ){ return; }
+ this->looking_for_new_block = true;
+
+ if( this->current_block != NULL ){ this->looking_for_new_block = false; return; }
+
+ // Return if queue is empty
+ if( this->queue.size() == 0 ){
+ this->current_block = NULL;
+ // TODOÂ :Â ON_QUEUE_EMPTY event
+ this->looking_for_new_block = false;
+ return;
+ }
+
+ // Get a new block
+ this->current_block = this->queue.get_ref(0);
+
+ // Tell all modules about it
+ this->kernel->call_event(ON_BLOCK_BEGIN, this->current_block);
+
+ // In case the module was not taken
+ if( this->current_block->times_taken < 1 ){
+ this->looking_for_new_block = false;
+ this->current_block->release();
+ }
+
+ this->looking_for_new_block = false;
+
+}
+
+void Player::wait_for_queue(int free_blocks){
+ mbed::Timer t;
+ while( this->queue.size() >= this->queue.capacity()-free_blocks ){
+ t.reset();
+ t.start();
+ this->kernel->call_event(ON_IDLE);
+ t.stop();
+ if(t.read_us() < 500)
+ wait_us(500 - t.read_us());
+ }
+}
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Player.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Player.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,33 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PLAYER_H
+#define PLAYER_H
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+using namespace std;
+#include <string>
+#include <vector>
+
+class Player : public Module {
+ public:
+ Player();
+
+ Block* new_block();
+ void new_block_added();
+ void pop_and_process_new_block(int debug);
+ void wait_for_queue(int free_blocks);
+
+ RingBuffer<Block,32> queue; // Queue of Blocks
+ Block* current_block;
+ bool looking_for_new_block;
+
+};
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Robot.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Robot.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,317 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl)
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include <string>
+using std::string;
+#include <math.h>
+#include "Planner.h"
+#include "Player.h"
+#include "Robot.h"
+#include "libs/nuts_bolts.h"
+#include "../communication/utils/Gcode.h"
+#include "arm_solutions/BaseSolution.h"
+#include "arm_solutions/CartesianSolution.h"
+
+Robot::Robot(){
+ this->inch_mode = false;
+ this->absolute_mode = true;
+ this->motion_mode = MOTION_MODE_SEEK;
+ this->select_plane(X_AXIS, Y_AXIS, Z_AXIS);
+ clear_vector(this->current_position);
+ clear_vector(this->last_milestone);
+}
+
+//Called when the module has just been loaded
+void Robot::on_module_loaded() {
+ this->arm_solution = new CartesianSolution(this->kernel->config);
+ this->register_for_event(ON_GCODE_RECEIVED);
+
+ // Configuration
+ this->on_config_reload(this);
+}
+
+void Robot::on_config_reload(void* argument){
+ this->feed_rate = this->kernel->config->value(default_feed_rate_checksum )->by_default(100)->as_number()/60;
+ this->seek_rate = this->kernel->config->value(default_seek_rate_checksum )->by_default(100)->as_number()/60;
+ this->mm_per_line_segment = this->kernel->config->value(mm_per_line_segment_checksum)->by_default(0.1)->as_number();
+ this->mm_per_arc_segment = this->kernel->config->value(mm_per_arc_segment_checksum )->by_default(10 )->as_number();
+ this->arc_correction = this->kernel->config->value(arc_correction_checksum )->by_default(5 )->as_number();
+ this->max_speeds[X_AXIS] = this->kernel->config->value(x_axis_max_speed_checksum )->by_default(0 )->as_number();
+ this->max_speeds[Y_AXIS] = this->kernel->config->value(y_axis_max_speed_checksum )->by_default(0 )->as_number();
+ this->max_speeds[Z_AXIS] = this->kernel->config->value(z_axis_max_speed_checksum )->by_default(0 )->as_number();
+}
+
+//A GCode has been received
+void Robot::on_gcode_received(void * argument){
+ Gcode* gcode = static_cast<Gcode*>(argument);
+ gcode->call_on_gcode_execute_event_immediatly = false;
+ gcode->on_gcode_execute_event_called = false;
+ //If the queue is empty, execute immediatly, otherwise attach to the last added block
+ if( this->kernel->player->queue.size() == 0 ){
+ gcode->call_on_gcode_execute_event_immediatly = true;
+ this->execute_gcode(gcode);
+ if( gcode->on_gcode_execute_event_called == false ){
+ this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
+ }
+ }else{
+ Block* block = this->kernel->player->queue.get_ref( this->kernel->player->queue.size() - 1 );
+ this->execute_gcode(gcode);
+ block->append_gcode(gcode);
+ }
+
+}
+
+
+//See if the current Gcode line has some orders for us
+void Robot::execute_gcode(Gcode* gcode){
+
+ //Temp variables, constant properties are stored in the object
+ uint8_t next_action = NEXT_ACTION_DEFAULT;
+ this->motion_mode = -1;
+
+ //G-letter Gcodes are mostly what the Robot module is interrested in, other modules also catch the gcode event and do stuff accordingly
+ if( gcode->has_letter('G')){
+ switch( (int) gcode->get_value('G') ){
+ case 0: this->motion_mode = MOTION_MODE_SEEK; break;
+ case 1: this->motion_mode = MOTION_MODE_LINEAR; break;
+ case 2: this->motion_mode = MOTION_MODE_CW_ARC; break;
+ case 3: this->motion_mode = MOTION_MODE_CCW_ARC; break;
+ case 17: this->select_plane(X_AXIS, Y_AXIS, Z_AXIS); break;
+ case 18: this->select_plane(X_AXIS, Z_AXIS, Y_AXIS); break;
+ case 19: this->select_plane(Y_AXIS, Z_AXIS, X_AXIS); break;
+ case 20:this->inch_mode = true; break;
+ case 21:this->inch_mode = false; break;
+ case 90:this->absolute_mode = true; break;
+ case 91:this->absolute_mode = false; break;
+ }
+ }else{ return; }
+
+ //Get parameters
+ double target[3], offset[3];
+ clear_vector(target); clear_vector(offset);
+
+ memcpy(target, this->current_position, sizeof(target)); //default to last target
+
+ for(char letter = 'I'; letter <= 'K'; letter++){ if( gcode->has_letter(letter) ){ offset[letter-'I'] = this->to_millimeters(gcode->get_value(letter)); } }
+ for(char letter = 'X'; letter <= 'Z'; letter++){ if( gcode->has_letter(letter) ){ target[letter-'X'] = this->to_millimeters(gcode->get_value(letter)) + ( this->absolute_mode ? 0 : target[letter-'X']); } }
+
+ if( gcode->has_letter('F') ){ if( this->motion_mode == MOTION_MODE_SEEK ){ this->seek_rate = this->to_millimeters( gcode->get_value('F') ) / 60; }else{ this->feed_rate = this->to_millimeters( gcode->get_value('F') ) / 60; } }
+
+ //Perform any physical actions
+ switch( next_action ){
+ case NEXT_ACTION_DEFAULT:
+ switch(this->motion_mode){
+ case MOTION_MODE_CANCEL: break;
+ case MOTION_MODE_SEEK : this->append_line(gcode, target, this->seek_rate ); break;
+ case MOTION_MODE_LINEAR: this->append_line(gcode, target, this->feed_rate ); break;
+ case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: this->compute_arc(gcode, offset, target ); break;
+ }
+ break;
+ }
+
+ // As far as the parser is concerned, the position is now == target. In reality the
+ // motion control system might still be processing the action and the real tool position
+ // in any intermediate location.
+ memcpy(this->current_position, target, sizeof(double)*3); // this->position[] = target[];
+
+}
+
+// Convert target from millimeters to steps, and append this to the planner
+void Robot::append_milestone( double target[], double rate ){
+ int steps[3]; //Holds the result of the conversion
+
+ this->arm_solution->millimeters_to_steps( target, steps );
+
+ double deltas[3];
+ for(int axis=X_AXIS;axis<=Z_AXIS;axis++){deltas[axis]=target[axis]-this->last_milestone[axis];}
+
+
+ double millimeters_of_travel = sqrt( pow( deltas[X_AXIS], 2 ) + pow( deltas[Y_AXIS], 2 ) + pow( deltas[Z_AXIS], 2 ) );
+
+ double duration = 0;
+ if( rate > 0 ){ duration = millimeters_of_travel / rate; }
+
+ for(int axis=X_AXIS;axis<=Z_AXIS;axis++){
+ if( this->max_speeds[axis] > 0 ){
+ double axis_speed = ( fabs(deltas[axis]) / ( millimeters_of_travel / rate )) * 60;
+ if( axis_speed > this->max_speeds[axis] ){
+ rate = rate * ( this->max_speeds[axis] / axis_speed );
+ }
+ }
+ }
+
+ this->kernel->planner->append_block( steps, rate*60, millimeters_of_travel, deltas );
+
+ memcpy(this->last_milestone, target, sizeof(double)*3); // this->last_milestone[] = target[];
+
+}
+
+void Robot::append_line(Gcode* gcode, double target[], double rate ){
+
+
+ // We cut the line into smaller segments. This is not usefull in a cartesian robot, but necessary for robots with rotational axes.
+ // In cartesian robot, a high "mm_per_line_segment" setting will prevent waste.
+ gcode->millimeters_of_travel = sqrt( pow( target[X_AXIS]-this->current_position[X_AXIS], 2 ) + pow( target[Y_AXIS]-this->current_position[Y_AXIS], 2 ) + pow( target[Z_AXIS]-this->current_position[Z_AXIS], 2 ) );
+
+ if( gcode->call_on_gcode_execute_event_immediatly == true ){
+ this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
+ gcode->on_gcode_execute_event_called = true;
+ }
+
+ if (gcode->millimeters_of_travel == 0.0) {
+ this->append_milestone(this->current_position, 0.0);
+ return;
+ }
+
+ uint16_t segments = ceil( gcode->millimeters_of_travel/ this->mm_per_line_segment);
+ // A vector to keep track of the endpoint of each segment
+ double temp_target[3];
+ //Initialize axes
+ memcpy( temp_target, this->current_position, sizeof(double)*3); // temp_target[] = this->current_position[];
+
+ //For each segment
+ for( int i=0; i<segments-1; i++ ){
+ for(int axis=X_AXIS; axis <= Z_AXIS; axis++ ){ temp_target[axis] += ( target[axis]-this->current_position[axis] )/segments; }
+ this->append_milestone(temp_target, rate);
+ }
+ this->append_milestone(target, rate);
+}
+
+
+void Robot::append_arc(Gcode* gcode, double target[], double offset[], double radius, bool is_clockwise ){
+
+ double center_axis0 = this->current_position[this->plane_axis_0] + offset[this->plane_axis_0];
+ double center_axis1 = this->current_position[this->plane_axis_1] + offset[this->plane_axis_1];
+ double linear_travel = target[this->plane_axis_2] - this->current_position[this->plane_axis_2];
+ double r_axis0 = -offset[this->plane_axis_0]; // Radius vector from center to current location
+ double r_axis1 = -offset[this->plane_axis_1];
+ double rt_axis0 = target[this->plane_axis_0] - center_axis0;
+ double rt_axis1 = target[this->plane_axis_1] - center_axis1;
+
+ // CCW angle between position and target from circle center. Only one atan2() trig computation required.
+ double angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1);
+ if (angular_travel < 0) { angular_travel += 2*M_PI; }
+ if (is_clockwise) { angular_travel -= 2*M_PI; }
+
+ gcode->millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
+
+ if( gcode->call_on_gcode_execute_event_immediatly == true ){
+ this->kernel->call_event(ON_GCODE_EXECUTE, gcode );
+ gcode->on_gcode_execute_event_called = true;
+ }
+
+ if (gcode->millimeters_of_travel == 0.0) {
+ this->append_milestone(this->current_position, 0.0);
+ return;
+ }
+
+ uint16_t segments = floor(gcode->millimeters_of_travel/this->mm_per_arc_segment);
+
+ double theta_per_segment = angular_travel/segments;
+ double linear_per_segment = linear_travel/segments;
+
+ /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
+ and phi is the angle of rotation. Based on the solution approach by Jens Geisler.
+ r_T = [cos(phi) -sin(phi);
+ sin(phi) cos(phi] * r ;
+ For arc generation, the center of the circle is the axis of rotation and the radius vector is
+ defined from the circle center to the initial position. Each line segment is formed by successive
+ vector rotations. This requires only two cos() and sin() computations to form the rotation
+ matrix for the duration of the entire arc. Error may accumulate from numerical round-off, since
+ all double numbers are single precision on the Arduino. (True double precision will not have
+ round off issues for CNC applications.) Single precision error can accumulate to be greater than
+ tool precision in some cases. Therefore, arc path correction is implemented.
+
+ Small angle approximation may be used to reduce computation overhead further. This approximation
+ holds for everything, but very small circles and large mm_per_arc_segment values. In other words,
+ theta_per_segment would need to be greater than 0.1 rad and N_ARC_CORRECTION would need to be large
+ to cause an appreciable drift error. N_ARC_CORRECTION~=25 is more than small enough to correct for
+ numerical drift error. N_ARC_CORRECTION may be on the order a hundred(s) before error becomes an
+ issue for CNC machines with the single precision Arduino calculations.
+ This approximation also allows mc_arc to immediately insert a line segment into the planner
+ without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied
+ a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead.
+ This is important when there are successive arc motions.
+ */
+ // Vector rotation matrix values
+ double cos_T = 1-0.5*theta_per_segment*theta_per_segment; // Small angle approximation
+ double sin_T = theta_per_segment;
+
+ double arc_target[3];
+ double sin_Ti;
+ double cos_Ti;
+ double r_axisi;
+ uint16_t i;
+ int8_t count = 0;
+
+ // Initialize the linear axis
+ arc_target[this->plane_axis_2] = this->current_position[this->plane_axis_2];
+
+ for (i = 1; i<segments; i++) { // Increment (segments-1)
+
+ if (count < this->arc_correction ) {
+ // Apply vector rotation matrix
+ r_axisi = r_axis0*sin_T + r_axis1*cos_T;
+ r_axis0 = r_axis0*cos_T - r_axis1*sin_T;
+ r_axis1 = r_axisi;
+ count++;
+ } else {
+ // Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
+ // Compute exact location by applying transformation matrix from initial radius vector(=-offset).
+ cos_Ti = cos(i*theta_per_segment);
+ sin_Ti = sin(i*theta_per_segment);
+ r_axis0 = -offset[this->plane_axis_0]*cos_Ti + offset[this->plane_axis_1]*sin_Ti;
+ r_axis1 = -offset[this->plane_axis_0]*sin_Ti - offset[this->plane_axis_1]*cos_Ti;
+ count = 0;
+ }
+
+ // Update arc_target location
+ arc_target[this->plane_axis_0] = center_axis0 + r_axis0;
+ arc_target[this->plane_axis_1] = center_axis1 + r_axis1;
+ arc_target[this->plane_axis_2] += linear_per_segment;
+ this->append_milestone(arc_target, this->feed_rate);
+
+ }
+ // Ensure last segment arrives at target location.
+ this->append_milestone(target, this->feed_rate);
+}
+
+
+void Robot::compute_arc(Gcode* gcode, double offset[], double target[]){
+
+ // Find the radius
+ double radius = hypot(offset[this->plane_axis_0], offset[this->plane_axis_1]);
+
+ // Set clockwise/counter-clockwise sign for mc_arc computations
+ bool is_clockwise = false;
+ if( this->motion_mode == MOTION_MODE_CW_ARC ){ is_clockwise = true; }
+
+ // Append arc
+ this->append_arc(gcode, target, offset, radius, is_clockwise );
+
+}
+
+
+// Convert from inches to millimeters ( our internal storage unit ) if needed
+inline double Robot::to_millimeters( double value ){
+ return this->inch_mode ? value/25.4 : value;
+}
+
+double Robot::theta(double x, double y){
+ double t = atan(x/fabs(y));
+ if (y>0) {return(t);} else {if (t>0){return(M_PI-t);} else {return(-M_PI-t);}}
+}
+
+void Robot::select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2){
+ this->plane_axis_0 = axis_0;
+ this->plane_axis_1 = axis_1;
+ this->plane_axis_2 = axis_2;
+}
+
+
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Robot.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Robot.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,92 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ROBOT_H
+#define ROBOT_H
+
+#include <string>
+using std::string;
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "../communication/utils/Gcode.h"
+#include "arm_solutions/BaseSolution.h"
+#include "Planner.h"
+
+#define default_seek_rate_checksum 6633
+#define default_feed_rate_checksum 47357
+#define mm_per_line_segment_checksum 30176
+#define mm_per_arc_segment_checksum 15470
+#define arc_correction_checksum 5074
+#define x_axis_max_speed_checksum 64935
+#define y_axis_max_speed_checksum 3752
+#define z_axis_max_speed_checksum 7849
+
+#define NEXT_ACTION_DEFAULT 0
+#define NEXT_ACTION_DWELL 1
+#define NEXT_ACTION_GO_HOME 2
+
+#define MOTION_MODE_SEEK 0 // G0
+#define MOTION_MODE_LINEAR 1 // G1
+#define MOTION_MODE_CW_ARC 2 // G2
+#define MOTION_MODE_CCW_ARC 3 // G3
+#define MOTION_MODE_CANCEL 4 // G80
+
+#define PATH_CONTROL_MODE_EXACT_PATH 0
+#define PATH_CONTROL_MODE_EXACT_STOP 1
+#define PATH_CONTROL_MODE_CONTINOUS 2
+
+#define PROGRAM_FLOW_RUNNING 0
+#define PROGRAM_FLOW_PAUSED 1
+#define PROGRAM_FLOW_COMPLETED 2
+
+#define SPINDLE_DIRECTION_CW 0
+#define SPINDLE_DIRECTION_CCW 1
+
+#define M_PI 3.14159
+
+
+class Robot : public Module {
+ public:
+ Robot();
+ virtual void on_module_loaded();
+ virtual void on_config_reload(void* argument);
+ virtual void on_gcode_received(void* argument);
+ void execute_gcode(Gcode* gcode);
+ void append_milestone( double target[], double feed_rate);
+ void append_line( Gcode* gcode, double target[], double feed_rate);
+ //void append_arc(double theta_start, double angular_travel, double radius, double depth, double rate);
+ void append_arc( Gcode* gcode, double target[], double offset[], double radius, bool is_clockwise );
+
+
+ void compute_arc(Gcode* gcode, double offset[], double target[]);
+ double to_millimeters(double value);
+ double theta(double x, double y);
+ void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2);
+
+ double current_position[3]; // Current position, in millimeters
+ double last_milestone[3]; // Last position, in millimeters
+ bool inch_mode; // true for inch mode, false for millimeter mode ( default )
+ bool absolute_mode; // true for absolute mode ( default ), false for relative mode
+ int8_t motion_mode; // Motion mode for the current received Gcode
+ double seek_rate; // Current rate for seeking moves ( mm/s )
+ double feed_rate; // Current rate for feeding moves ( mm/s )
+ uint8_t plane_axis_0, plane_axis_1, plane_axis_2; // Current plane ( XY, XZ, YZ )
+ BaseSolution* arm_solution; // Selected Arm solution ( millimeters to step calculation )
+ double mm_per_line_segment; // Setting : Used to split lines into segments
+ double mm_per_arc_segment; // Setting : Used to split arcs into segmentrs
+
+ // Number of arc generation iterations by small angle approximation before exact arc trajectory
+ // correction. This parameter maybe decreased if there are issues with the accuracy of the arc
+ // generations. In general, the default value is more than enough for the intended CNC applications
+ // of grbl, and should be on the order or greater than the size of the buffer to help with the
+ // computational efficiency of generating arcs.
+ int arc_correction; // Setting : how often to rectify arc computation
+ double max_speeds[3]; // Setting : max allowable speed in mm/m for each axis
+
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Stepper.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Stepper.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,243 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl)
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "Stepper.h"
+#include "Planner.h"
+#include "Player.h"
+#include <vector>
+using namespace std;
+#include "libs/nuts_bolts.h"
+
+Stepper* stepper;
+
+Stepper::Stepper(){
+ this->current_block = NULL;
+ this->step_events_completed = 0;
+ this->divider = 0;
+ this->paused = false;
+}
+
+//Called when the module has just been loaded
+void Stepper::on_module_loaded(){
+ stepper = this;
+ this->register_for_event(ON_BLOCK_BEGIN);
+ this->register_for_event(ON_BLOCK_END);
+ this->register_for_event(ON_GCODE_EXECUTE);
+ this->register_for_event(ON_PLAY);
+ this->register_for_event(ON_PAUSE);
+
+ // Get onfiguration
+ this->on_config_reload(this);
+
+ // Acceleration ticker
+ //this->kernel->slow_ticker->set_frequency(this->acceleration_ticks_per_second/10);
+ this->kernel->slow_ticker->attach( this->acceleration_ticks_per_second, this, &Stepper::trapezoid_generator_tick );
+
+ // Initiate main_interrupt timer and step reset timer
+ this->kernel->step_ticker->attach( this, &Stepper::main_interrupt );
+ this->kernel->step_ticker->reset_attach( this, &Stepper::reset_step_pins );
+
+}
+
+// Get configuration from the config file
+void Stepper::on_config_reload(void* argument){
+
+ this->microseconds_per_step_pulse = this->kernel->config->value(microseconds_per_step_pulse_checksum )->by_default(5 )->as_number();
+ this->acceleration_ticks_per_second = this->kernel->config->value(acceleration_ticks_per_second_checksum)->by_default(100 )->as_number();
+ this->minimum_steps_per_minute = this->kernel->config->value(minimum_steps_per_minute_checksum )->by_default(1200 )->as_number();
+ this->base_stepping_frequency = this->kernel->config->value(base_stepping_frequency_checksum )->by_default(100000)->as_number();
+ this->alpha_step_pin = this->kernel->config->value(alpha_step_pin_checksum )->by_default("1.21" )->as_pin()->as_output();
+ this->beta_step_pin = this->kernel->config->value(beta_step_pin_checksum )->by_default("1.23" )->as_pin()->as_output();
+ this->gamma_step_pin = this->kernel->config->value(gamma_step_pin_checksum )->by_default("1.22!" )->as_pin()->as_output();
+ this->alpha_dir_pin = this->kernel->config->value(alpha_dir_pin_checksum )->by_default("1.18" )->as_pin()->as_output();
+ this->beta_dir_pin = this->kernel->config->value(beta_dir_pin_checksum )->by_default("1.20" )->as_pin()->as_output();
+ this->gamma_dir_pin = this->kernel->config->value(gamma_dir_pin_checksum )->by_default("1.19" )->as_pin()->as_output();
+ this->alpha_en_pin = this->kernel->config->value(alpha_en_pin_checksum )->by_default("0.4" )->as_pin()->as_output()->as_open_drain();
+ this->beta_en_pin = this->kernel->config->value(beta_en_pin_checksum )->by_default("0.10" )->as_pin()->as_output()->as_open_drain();
+ this->gamma_en_pin = this->kernel->config->value(gamma_en_pin_checksum )->by_default("0.19" )->as_pin()->as_output()->as_open_drain();
+
+
+ // TODO :Â This is supposed to be done by gcodes
+ this->alpha_en_pin->set(0);
+ this->beta_en_pin->set(0);
+ this->gamma_en_pin->set(0);
+
+
+ // Set the Timer interval for Match Register 1,
+ this->kernel->step_ticker->set_reset_delay( this->microseconds_per_step_pulse / 1000000 );
+ this->kernel->step_ticker->set_frequency( this->base_stepping_frequency );
+}
+
+// When the play/pause button is set to pause, or a module calls the ON_PAUSE event
+void Stepper::on_pause(void* argument){
+ this->paused = true;
+}
+
+// When the play/pause button is set to play, or a module calls the ON_PLAY event
+void Stepper::on_play(void* argument){
+ // TODO:Â Re-compute the whole queue for a cold-start
+ this->paused = false;
+}
+
+void Stepper::on_gcode_execute(void* argument){
+ Gcode* gcode = static_cast<Gcode*>(argument);
+
+ if( gcode->has_letter('M')){
+ int code = (int) gcode->get_value('M');
+ if( code == 84 ){
+ this->alpha_en_pin->set(0);
+ this->beta_en_pin->set(0);
+ this->gamma_en_pin->set(0);
+ }
+ }
+}
+
+// AÂ new block is popped from the queue
+void Stepper::on_block_begin(void* argument){
+ Block* block = static_cast<Block*>(argument);
+
+ // The stepper does not care about 0-blocks
+ if( block->millimeters == 0.0 ){ return; }
+
+ // Mark the new block as of interrest to us
+ block->take();
+
+ // Setup
+ for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){ this->counters[stpr] = 0; this->stepped[stpr] = 0; }
+ this->step_events_completed = 0;
+
+ this->current_block = block;
+
+ // This is just to save computing power and not do it every step
+ this->update_offsets();
+
+ // Setup acceleration for this block
+ this->trapezoid_generator_reset();
+
+}
+
+// Current block is discarded
+void Stepper::on_block_end(void* argument){
+ Block* block = static_cast<Block*>(argument);
+ this->current_block = NULL; //stfu !
+}
+
+// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Smoothie. It is executed at the rate set with
+// config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately.
+inline uint32_t Stepper::main_interrupt(uint32_t dummy){
+ if( this->paused ){ return 0; }
+
+ // Step dir pins first, then step pinse, stepper drivers like to know the direction before the step signal comes in
+ this->alpha_dir_pin->set( ( this->out_bits >> 0 ) & 1 );
+ this->beta_dir_pin->set( ( this->out_bits >> 1 ) & 1 );
+ this->gamma_dir_pin->set( ( this->out_bits >> 2 ) & 1 );
+ this->alpha_step_pin->set( ( this->out_bits >> 3 ) & 1 );
+ this->beta_step_pin->set( ( this->out_bits >> 4 ) & 1 );
+ this->gamma_step_pin->set( ( this->out_bits >> 5 ) & 1 );
+
+ if( this->current_block != NULL ){
+ // Set bits for direction and steps
+ this->out_bits = this->current_block->direction_bits;
+ for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){
+ this->counters[stpr] += this->counter_increment;
+ if( this->counters[stpr] > this->offsets[stpr] && this->stepped[stpr] < this->current_block->steps[stpr] ){
+ this->counters[stpr] -= this->offsets[stpr] ;
+ this->stepped[stpr]++;
+ this->out_bits |= (1 << (stpr+3) );
+ }
+ }
+ // If current block is finished, reset pointer
+ this->step_events_completed += this->counter_increment;
+ if( this->step_events_completed >= this->current_block->steps_event_count<<16 ){
+ if( this->stepped[ALPHA_STEPPER] == this->current_block->steps[ALPHA_STEPPER] && this->stepped[BETA_STEPPER] == this->current_block->steps[BETA_STEPPER] && this->stepped[GAMMA_STEPPER] == this->current_block->steps[GAMMA_STEPPER] ){
+ if( this->current_block != NULL ){
+ this->current_block->release();
+ }
+ }
+ }
+ }else{
+ this->out_bits = 0;
+ }
+
+}
+
+// We compute this here instead of each time in the interrupt
+void Stepper::update_offsets(){
+ for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){
+ this->offsets[stpr] = (int)floor((float)((float)(1<<16)*(float)((float)this->current_block->steps_event_count / (float)this->current_block->steps[stpr])));
+ }
+}
+
+// This is called ACCELERATION_TICKS_PER_SECOND times per second by the step_event
+// interrupt. It can be assumed that the trapezoid-generator-parameters and the
+// current_block stays untouched by outside handlers for the duration of this function call.
+uint32_t Stepper::trapezoid_generator_tick( uint32_t dummy ) {
+ if(this->current_block && !this->trapezoid_generator_busy && !this->paused ) {
+ if(this->step_events_completed < this->current_block->accelerate_until<<16) {
+ this->trapezoid_adjusted_rate += this->current_block->rate_delta;
+ if (this->trapezoid_adjusted_rate > this->current_block->nominal_rate ) {
+ this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
+ }
+ this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ } else if (this->step_events_completed >= this->current_block->decelerate_after<<16) {
+ // NOTE: We will only reduce speed if the result will be > 0. This catches small
+ // rounding errors that might leave steps hanging after the last trapezoid tick.
+ if (this->trapezoid_adjusted_rate > double(this->current_block->rate_delta) * 1.5) {
+ this->trapezoid_adjusted_rate -= this->current_block->rate_delta;
+ }else{
+ this->trapezoid_adjusted_rate = double(this->current_block->rate_delta) * 1.5;
+ //this->trapezoid_adjusted_rate = floor(double(this->trapezoid_adjusted_rate / 2 ));
+ //this->kernel->serial->printf("over!\r\n");
+ }
+ if (this->trapezoid_adjusted_rate < this->current_block->final_rate ) {
+ this->trapezoid_adjusted_rate = this->current_block->final_rate;
+ }
+ this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ } else {
+ // Make sure we cruise at exactly nominal rate
+ if (this->trapezoid_adjusted_rate != this->current_block->nominal_rate) {
+ this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
+ this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ }
+ }
+ }
+}
+
+// Initializes the trapezoid generator from the current block. Called whenever a new
+// block begins.
+void Stepper::trapezoid_generator_reset(){
+ this->trapezoid_adjusted_rate = this->current_block->initial_rate;
+ this->trapezoid_tick_cycle_counter = 0;
+ // Because this can be called directly from the main loop, it could be interrupted by the acceleration ticker, and that would be bad, so we use a flag
+ this->trapezoid_generator_busy = true;
+ this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
+ this->trapezoid_generator_busy = false;
+}
+
+void Stepper::set_step_events_per_minute( double steps_per_minute ){
+
+ // We do not step slower than this
+ steps_per_minute = max(steps_per_minute, this->minimum_steps_per_minute);
+
+ // The speed factor is the factor by which we must multiply the minimal step frequency to reach the maximum step frequency
+ // The higher, the smoother the movement will be, but the closer the maximum and minimum frequencies are, the smaller the factor is
+ // speed_factor = base_stepping_frequency / steps_per_second
+ // counter_increment = 1 / speed_factor ( 1 is 1<<16 because we do fixed point )
+ this->counter_increment = int(floor(double(1<<16)/double(this->base_stepping_frequency / (steps_per_minute/60L))));
+
+ this->kernel->call_event(ON_SPEED_CHANGE, this);
+
+}
+
+uint32_t Stepper::reset_step_pins(uint32_t dummy){
+ this->alpha_step_pin->set(0);
+ this->beta_step_pin->set(0);
+ this->gamma_step_pin->set(0);
+
+}
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/Stepper.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/Stepper.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,83 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef STEPPER_H
+#define STEPPER_H
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "Planner.h"
+#include "Block.h"
+
+#define microseconds_per_step_pulse_checksum 42333
+#define acceleration_ticks_per_second_checksum 25075
+#define minimum_steps_per_minute_checksum 9003
+#define base_stepping_frequency_checksum 21918
+#define alpha_step_pin_checksum 11468
+#define beta_step_pin_checksum 22114
+#define gamma_step_pin_checksum 1225
+#define alpha_dir_pin_checksum 55887
+#define beta_dir_pin_checksum 28644
+#define gamma_dir_pin_checksum 46412
+#define alpha_en_pin_checksum 35042
+#define beta_en_pin_checksum 34680
+#define gamma_en_pin_checksum 26335
+
+
+class Stepper : public Module {
+ public:
+ Stepper();
+ virtual void on_module_loaded();
+ virtual void on_config_reload(void* argument);
+ virtual void on_block_begin(void* argument);
+ virtual void on_block_end(void* argument);
+ virtual void on_gcode_execute(void* argument);
+ virtual void on_play(void* argument);
+ virtual void on_pause(void* argument);
+ uint32_t main_interrupt(uint32_t dummy);
+ void trapezoid_generator_reset();
+ void set_step_events_per_minute(double steps_per_minute);
+ uint32_t trapezoid_generator_tick(uint32_t dummy);
+ uint32_t reset_step_pins(uint32_t dummy);
+ void update_offsets();
+ int config_step_timer( int cycles );
+
+ Block* current_block;
+ int counters[3];
+ int stepped[3];
+ int offsets[3];
+ float counter_alpha;
+ float counter_beta;
+ float counter_gamma;
+ int step_events_completed;
+ unsigned int out_bits;
+ double trapezoid_adjusted_rate;
+ int trapezoid_tick_cycle_counter;
+ int cycles_per_step_event;
+ bool trapezoid_generator_busy;
+ int microseconds_per_step_pulse;
+ int acceleration_ticks_per_second;
+ int divider;
+ int minimum_steps_per_minute;
+ int base_stepping_frequency;
+ Pin* alpha_step_pin;
+ Pin* beta_step_pin;
+ Pin* gamma_step_pin;
+ Pin* alpha_dir_pin;
+ Pin* beta_dir_pin;
+ Pin* gamma_dir_pin;
+ Pin* alpha_en_pin;
+ Pin* beta_en_pin;
+ Pin* gamma_en_pin;
+ unsigned short step_bits[3];
+ int counter_increment;
+ bool paused;
+};
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/arm_solutions/BaseSolution.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/arm_solutions/BaseSolution.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,6 @@
+#include "BaseSolution.h"
+
+BaseSolution::BaseSolution(){}
+
+void BaseSolution::millimeters_to_steps( double millimeters[], int steps[] ){}
+void BaseSolution::steps_to_millimeters( int steps[], double millimeters[] ){}
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/arm_solutions/BaseSolution.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/arm_solutions/BaseSolution.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,12 @@
+// Base class for an arm solution, only usefull for inheritence. http://en.wikipedia.org/wiki/Arm_solution
+#ifndef BASESOLUTION_H
+#define BASESOLUTION_H
+
+class BaseSolution {
+ public:
+ BaseSolution();
+ virtual void millimeters_to_steps( double millimeters[], int steps[] );
+ virtual void steps_to_millimeters( int steps[], double millimeters[] );
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/arm_solutions/CartesianSolution.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/arm_solutions/CartesianSolution.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,16 @@
+#include "CartesianSolution.h"
+#include <math.h>
+
+CartesianSolution::CartesianSolution(Config* passed_config) : config(passed_config){
+ this->alpha_steps_per_mm = this->config->value(alpha_steps_per_mm_checksum)->as_number();
+ this->beta_steps_per_mm = this->config->value( beta_steps_per_mm_checksum)->as_number();
+ this->gamma_steps_per_mm = this->config->value(gamma_steps_per_mm_checksum)->as_number();
+}
+
+void CartesianSolution::millimeters_to_steps( double millimeters[], int steps[] ){
+ steps[ALPHA_STEPPER] = floor( millimeters[X_AXIS] * this->alpha_steps_per_mm +0.5);
+ steps[BETA_STEPPER ] = floor( millimeters[Y_AXIS] * this->beta_steps_per_mm +0.5);
+ steps[GAMMA_STEPPER] = floor( millimeters[Z_AXIS] * this->gamma_steps_per_mm +0.5);
+}
+
+void CartesianSolution::steps_to_millimeters( int steps[], double millimeters[] ){}
diff -r 000000000000 -r 31e91bb0ef3c modules/robot/arm_solutions/CartesianSolution.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/robot/arm_solutions/CartesianSolution.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,31 @@
+#ifndef CARTESIANSOLUTION_H
+#define CARTESIANSOLUTION_H
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "BaseSolution.h"
+#include "libs/nuts_bolts.h"
+
+#include "libs/Config.h"
+
+#define alpha_steps_per_mm_checksum 46458
+#define beta_steps_per_mm_checksum 13840
+#define gamma_steps_per_mm_checksum 33143
+
+class CartesianSolution : public BaseSolution {
+ public:
+ CartesianSolution(Config* passed_config);
+ virtual void millimeters_to_steps( double millimeters[], int steps[] );
+ virtual void steps_to_millimeters( int steps[], double millimeters[] );
+
+ Config* config;
+ double alpha_steps_per_mm;
+ double beta_steps_per_mm;
+ double gamma_steps_per_mm;
+};
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/extruder/Extruder.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/extruder/Extruder.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,285 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "modules/robot/Player.h"
+#include "modules/robot/Block.h"
+#include "modules/tools/extruder/Extruder.h"
+
+#define extruder_step_pin_checksum 40763
+#define extruder_dir_pin_checksum 57277
+#define extruder_en_pin_checksum 8017
+
+Extruder::Extruder() {
+ this->absolute_mode = true;
+ this->direction = 1;
+ this->acceleration_lock = false;
+ this->step_counter = 0;
+ this->counter_increment = 0;
+ this->paused = false;
+}
+
+void Extruder::on_module_loaded() {
+
+ // Do not do anything if not enabledd
+ if( this->kernel->config->value( extruder_module_enable_checksum )->by_default(false)->as_bool() == false ){ return; }
+
+ // Settings
+ this->on_config_reload(this);
+
+ // We work on the same Block as Stepper, so we need to know when it gets a new one and drops one
+ this->register_for_event(ON_BLOCK_BEGIN);
+ this->register_for_event(ON_BLOCK_END);
+ this->register_for_event(ON_GCODE_EXECUTE);
+ this->register_for_event(ON_PLAY);
+ this->register_for_event(ON_PAUSE);
+
+ // Start values
+ this->start_position = 0;
+ this->target_position = 0;
+ this->current_position = 0;
+ this->current_block = NULL;
+ this->mode = OFF;
+
+ // Update speed every *acceleration_ticks_per_second*
+ // TODO:Â Make this an independent setting
+ this->kernel->slow_ticker->attach( this->kernel->stepper->acceleration_ticks_per_second , this, &Extruder::acceleration_tick );
+
+ // Initiate main_interrupt timer and step reset timer
+ this->kernel->step_ticker->attach( this, &Extruder::stepping_tick );
+ this->kernel->step_ticker->reset_attach( this, &Extruder::reset_step_pin );
+
+}
+
+// Get config
+void Extruder::on_config_reload(void* argument){
+ this->microseconds_per_step_pulse = this->kernel->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number();
+ this->steps_per_millimeter = this->kernel->config->value(extruder_steps_per_mm_checksum )->by_default(1)->as_number();
+ this->feed_rate = this->kernel->config->value(default_feed_rate_checksum )->by_default(1)->as_number();
+ this->acceleration = this->kernel->config->value(acceleration_checksum )->by_default(1)->as_number();
+
+ this->step_pin = this->kernel->config->value(extruder_step_pin_checksum )->by_default("1.22" )->as_pin()->as_output();
+ this->dir_pin = this->kernel->config->value(extruder_dir_pin_checksum )->by_default("1.19" )->as_pin()->as_output();
+ this->en_pin = this->kernel->config->value(extruder_en_pin_checksum )->by_default("0.19" )->as_pin()->as_output();
+}
+
+
+// When the play/pause button is set to pause, or a module calls the ON_PAUSE event
+void Extruder::on_pause(void* argument){
+ this->paused = true;
+}
+
+// When the play/pause button is set to play, or a module calls the ON_PLAY event
+void Extruder::on_play(void* argument){
+ this->paused = false;
+}
+
+
+
+// Compute extrusion speed based on parameters and gcode distance of travel
+void Extruder::on_gcode_execute(void* argument){
+ Gcode* gcode = static_cast<Gcode*>(argument);
+
+ // Absolute/relative mode
+ if( gcode->has_letter('M')){
+ int code = (int) gcode->get_value('M');
+ if( code == 82 ){ this->absolute_mode = true; }
+ if( code == 83 ){ this->absolute_mode = false; }
+ if( code == 84 ){ this->en_pin->set(0); }
+ }
+
+ // The mode is OFF by default, and SOLO or FOLLOW only if we need to extrude
+ this->mode = OFF;
+
+ if( gcode->has_letter('G') ){
+ // G92:Â Reset extruder position
+ if( gcode->get_value('G') == 92 ){
+ if( gcode->has_letter('E') ){
+ this->current_position = gcode->get_value('E');
+ this->target_position = this->current_position;
+ this->start_position = this->current_position;
+ }
+ }else{
+ // Extrusion length from 'G' Gcode
+ if( gcode->has_letter('E' )){
+ // Get relative extrusion distance depending on mode ( in absolute mode we must substract target_position )
+ double relative_extrusion_distance = gcode->get_value('E');
+ if( this->absolute_mode == true ){ relative_extrusion_distance = relative_extrusion_distance - this->target_position; }
+
+ // If the robot is moving, we follow it's movement, otherwise, we move alone
+ if( fabs(gcode->millimeters_of_travel) < 0.0001 ){ // With floating numbers, we can have 0 != 0 ... beeeh
+ this->mode = SOLO;
+ this->travel_distance = relative_extrusion_distance;
+ if( gcode->has_letter('F') ){ this->feed_rate = gcode->get_value('F'); }
+ }else{
+ this->mode = FOLLOW;
+ // We move proportionally to the robot's movement
+ this->travel_ratio = relative_extrusion_distance / gcode->millimeters_of_travel;
+ }
+
+ this->en_pin->set(1);
+ }
+ }
+ }
+
+}
+
+// When a new block begins, either follow the robot, or step by ourselves ( or stay back and do nothing )
+void Extruder::on_block_begin(void* argument){
+ Block* block = static_cast<Block*>(argument);
+ if( this->mode == SOLO ){
+ // In solo mode we take the block so we can move even if the stepper has nothing to do
+ block->take();
+ this->current_block = block;
+ this->start_position = this->target_position;
+ this->target_position = this->start_position + this->travel_distance ;
+ this->travel_ratio = 0.2; // TODOÂ :Â Make a real acceleration thing
+ if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; }
+ this->set_speed(int(floor((this->feed_rate/60)*this->steps_per_millimeter)));//Speed in steps per second
+ }else if( this->mode == FOLLOW ){
+ // In non-solo mode, we just follow the stepper module
+ this->current_block = block;
+ this->start_position = this->target_position;
+ this->target_position = this->start_position + ( this->current_block->millimeters * this->travel_ratio );
+ if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; }
+ this->acceleration_tick(0);
+ }
+
+}
+
+// When a block ends, pause the stepping interrupt
+void Extruder::on_block_end(void* argument){
+ Block* block = static_cast<Block*>(argument);
+ this->current_block = NULL;
+}
+
+// Called periodically to change the speed to match acceleration or to match the speed of the robot
+uint32_t Extruder::acceleration_tick(uint32_t dummy){
+
+ // Avoid trying to work when we really shouldn't ( between blocks or re-entry )
+ if( this->current_block == NULL || this->acceleration_lock || this->paused ){ return 0; }
+ this->acceleration_lock = true;
+
+ // In solo mode, we mode independently from the robot
+ if( this->mode == SOLO ){
+ // TODOÂ :Â Do real acceleration here
+ this->travel_ratio += 0.03;
+ if( this->travel_ratio > 1 ){ this->travel_ratio = 1; }
+ this->set_speed( int(floor(((this->feed_rate/60)*this->steps_per_millimeter)*this->travel_ratio)) ); // Speed in steps per second
+
+ // In follow mode we match the speed of the robot, + eventually advance
+ }else if( this->mode == FOLLOW ){
+ Stepper* stepper = this->kernel->stepper; // Just for convenience
+
+ // Strategy :
+ // * Find where in the block will the stepper be at the next tick ( if the block will have ended then, don't change speed )
+ // * Find what position this is for us
+ // * Find what speed we must go at to be at that position for the next acceleration tick
+ // TODOÂ :Â This works, but PLEASEÂ PLEASEÂ PLEASEÂ if you know a better way to do it, do it better, I don't find this elegant at all, it's just the best IÂ could think of
+ // UPDATE:Â Yes, this sucks, I have ideas on how to do it better. If this is really bugging you, open a ticket and I'll make it a priority
+
+ int ticks_forward = 3;
+ // We need to take those values here, and then use those instead of the live values, because using the live values inside the loop can break things ( infinite loops etc ... )
+ double next_stepper_rate = stepper->trapezoid_adjusted_rate;
+ double step_events_completed = (double(double(stepper->step_events_completed)/double(1<<16)));
+ double position = ( this->current_position - this->start_position ) * this->direction ;
+ double length = fabs( this->start_position - this->target_position );
+ double last_ratio = -1;
+
+ // Do the startegy above, but if it does not work, look a bit further and try again, and again ...
+ while(1){
+
+ // Find the position where we should be at the next tick
+ double next_ratio = double( step_events_completed + ( next_stepper_rate / 60 / ((double(stepper->acceleration_ticks_per_second)/ticks_forward)) ) ) / double( this->current_block->steps_event_count );
+ double next_relative_position = ( length * next_ratio );
+
+ // Advance
+ // TODO:Â Proper advance configuration
+ double advance = double(next_stepper_rate) * ( 0.00001 * 0.15 ) * 0.4 ;
+ //double advance = 0;
+ next_relative_position += ( advance );
+
+ // TODOÂ :Â all of those "if->return" is very hacky, we should do the math in a way where most of those don't happen, but that requires doing tons of drawing ...
+ if( last_ratio == next_ratio ){ this->acceleration_lock = false; return 0; }else{ last_ratio = next_ratio; }
+ if( next_ratio == 0 || next_ratio > 1 ){ this->acceleration_lock = false; return 0; }
+ if( ticks_forward > 1000 ){ this->acceleration_lock = false; return 0; } // This is very ugly
+
+ // Hack :Â We have not looked far enough, we compute how far ahead we must look to get a relevant value
+ if( position > next_relative_position ){
+ double far_back = position - next_relative_position;
+ double far_back_ratio = far_back / length;
+ double move_duration = double( this->current_block->steps_event_count ) / ( double(next_stepper_rate) / 60 ) ;
+ double ticks_in_a_move = floor( stepper->acceleration_ticks_per_second * move_duration +0.5);
+ double ratio_per_tick = 1 / ticks_in_a_move;
+ double ticks_to_equilibrium = ceil(far_back_ratio / ratio_per_tick) + 1;
+ ticks_forward += ticks_to_equilibrium;
+ // Because this is a loop, and we can be interrupted by the stepping interrupt, if that interrupt changes block, the new block may not be solo, and we may get trapped into an infinite loop
+ if( this->mode != FOLLOW ){ this->acceleration_lock = false; return 0; }
+ continue;
+ }
+
+ // Finally, compute the speed to get to that next position
+ double next_absolute_position = this->start_position + ( this->direction * next_relative_position );
+ double steps_to_next_tick = ( next_relative_position - position ) * this->steps_per_millimeter;
+ double speed_to_next_tick = steps_to_next_tick / ( 1 / double(double(this->kernel->stepper->acceleration_ticks_per_second) / ticks_forward) );
+
+ // Change stepping speed
+ this->set_speed( speed_to_next_tick );
+
+ this->acceleration_lock = false;
+ return 0;
+ }
+ }
+
+ this->acceleration_lock = false;
+ return 0;
+}
+
+// Convenience function to set stepping speed
+void Extruder::set_speed( int steps_per_second ){
+
+ if( steps_per_second < 10 ){ steps_per_second = 10; }
+
+ // TODOÂ :Â Proper limit config value
+ if( steps_per_second > (this->feed_rate*double(this->steps_per_millimeter))/60 ){
+ steps_per_second = (this->feed_rate*double(this->steps_per_millimeter))/60;
+ }
+
+ this->counter_increment = int(floor(double(1<<16)/double(this->kernel->stepper->base_stepping_frequency / steps_per_second)));
+
+}
+
+inline uint32_t Extruder::stepping_tick(uint32_t dummy){
+ if( this->paused ){ return 0; }
+
+ this->step_counter += this->counter_increment;
+ if( this->step_counter > 1<<16 ){
+ this->step_counter -= 1<<16;
+
+ // If we still have steps to do
+ // TODO:Â Step using the same timer as the robot, and count steps instead of absolute float position
+ if( ( this->current_position < this->target_position && this->direction == 1 ) || ( this->current_position > this->target_position && this->direction == -1 ) ){
+ this->current_position += (double(double(1)/double(this->steps_per_millimeter)))*double(this->direction);
+ this->dir_pin->set((this->direction > 0) ? 1 : 0);
+ this->step_pin->set(1);
+ }else{
+ // Move finished
+ if( this->mode == SOLO && this->current_block != NULL ){
+ // In follow mode, the robot takes and releases the block, in solo mode we do
+ this->current_block->release();
+ }
+ }
+ }
+ return 0;
+}
+
+uint32_t Extruder::reset_step_pin(uint32_t dummy){
+ this->step_pin->set(0);
+ return 0;
+}
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/extruder/Extruder.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/extruder/Extruder.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,74 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+
+#ifndef EXTURDER_MODULE_H
+#define EXTRUDER_MODULE_H
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "modules/robot/Block.h"
+
+#define microseconds_per_step_pulse_checksum 42333
+#define extruder_module_enable_checksum 6183
+#define extruder_steps_per_mm_checksum 58088
+#define default_feed_rate_checksum 53183
+#define acceleration_checksum 60356
+
+#define OFF 0
+#define SOLO 1
+#define FOLLOW 2
+
+class Extruder : public Module{
+ public:
+ Extruder();
+ virtual void on_module_loaded();
+ virtual void on_config_reload(void* argument);
+ virtual void on_gcode_execute(void* argument);
+ virtual void on_block_begin(void* argument);
+ virtual void on_block_end(void* argument);
+ virtual void on_play(void* argument);
+ virtual void on_pause(void* argument);
+ void set_speed(int steps_per_second);
+ uint32_t acceleration_tick(uint32_t dummy);
+ uint32_t stepping_tick(uint32_t dummy);
+ uint32_t reset_step_pin(uint32_t dummy);
+
+ Pin* step_pin; // Step pin for the stepper driver
+ Pin* dir_pin; // Dir pin for the stepper driver
+ Pin* en_pin;
+
+ double start_position; // Start point ( in steps ) for the current move
+ double target_position; // End point ( in steps ) for the current move
+ double current_position; // Current point ( in steps ) for the current move, incremented every time a step is outputed
+ Block* current_block; // Current block we are stepping, same as Stepper's one
+ int microseconds_per_step_pulse; // Pulse duration for step pulses
+ double steps_per_millimeter; // Steps to travel one millimeter
+ double feed_rate; //
+ double acceleration; //
+
+ int counter_increment;
+ int step_counter;
+
+ bool solo_mode;
+ double travel_ratio;
+ double travel_distance;
+ bool absolute_mode;
+
+ int direction;
+
+ bool debug;
+ int debug_count;
+
+ char mode;
+ bool acceleration_lock;
+
+ bool paused;
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/laser/Laser.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/laser/Laser.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,73 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "modules/communication/utils/Gcode.h"
+#include "modules/robot/Stepper.h"
+#include "Laser.h"
+#include "libs/nuts_bolts.h"
+
+Laser::Laser(PinName pin) : laser_pin(pin){
+ this->laser_pin.period_us(10);
+}
+
+void Laser::on_module_loaded() {
+ if( !this->kernel->config->value( laser_module_enable_checksum )->by_default(false)->as_bool() ){ return; }
+ this->register_for_event(ON_GCODE_EXECUTE);
+ this->register_for_event(ON_SPEED_CHANGE);
+ this->register_for_event(ON_PLAY);
+ this->register_for_event(ON_PAUSE);
+ this->register_for_event(ON_BLOCK_BEGIN);
+ this->register_for_event(ON_BLOCK_END);
+}
+
+// Turn laser off laser at the end of a move
+void Laser::on_block_end(void* argument){
+ this->laser_pin = 0;
+}
+
+// Set laser power at the beginning of a block
+void Laser::on_block_begin(void* argument){
+ this->set_proportional_power();
+}
+
+// When the play/pause button is set to pause, or a module calls the ON_PAUSE event
+void Laser::on_pause(void* argument){
+ this->laser_pin = 0;
+}
+
+// When the play/pause button is set to play, or a module calls the ON_PLAY event
+void Laser::on_play(void* argument){
+ this->set_proportional_power();
+}
+
+// Turn laser on/off depending on received GCodes
+void Laser::on_gcode_execute(void* argument){
+ Gcode* gcode = static_cast<Gcode*>(argument);
+ this->laser_on = false;
+ if( gcode->has_letter('G' )){
+ int code = gcode->get_value('G');
+ if( code == 0 ){ // G0
+ this->laser_pin = 0;
+ this->laser_on = false;
+ }else if( code >= 1 && code <= 3 ){ // G1, G2, G3
+ this->laser_on = true;
+ }
+ }
+}
+
+// We follow the stepper module here, so speed must be proportional
+void Laser::on_speed_change(void* argument){
+ this->set_proportional_power();
+}
+
+void Laser::set_proportional_power(){
+ if( this->laser_on && this->kernel->stepper->current_block ){
+ this->laser_pin = double(this->kernel->stepper->trapezoid_adjusted_rate)/double(this->kernel->stepper->current_block->nominal_rate);
+ }
+}
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/laser/Laser.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/laser/Laser.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,51 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef LASER_MODULE_H
+#define LASER_MODULE_H
+
+#include "libs/Module.h"
+#include "PwmOut.h" // mbed.h lib
+#include "libs/Kernel.h"
+#include "modules/communication/utils/Gcode.h"
+
+
+#define laser_module_enable_checksum 35529
+
+class Laser : public Module{
+ public:
+ Laser(PinName pin);
+ virtual void on_module_loaded();
+ virtual void on_block_end(void* argument);
+ virtual void on_block_begin(void* argument);
+ virtual void on_play(void* argument);
+ virtual void on_pause(void* argument);
+ virtual void on_gcode_execute(void* argument);
+ virtual void on_speed_change(void* argument);
+ void set_proportional_power();
+
+ mbed::PwmOut laser_pin; // PWM output to regulate the laser power
+ bool laser_on; // Laser status
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/spindle/Spindle.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/spindle/Spindle.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,81 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include "modules/communication/utils/Gcode.h"
+#include "modules/robot/Stepper.h"
+#include "Spindle.h"
+#include "libs/nuts_bolts.h"
+
+Spindle::Spindle(){
+
+}
+
+void Spindle::on_module_loaded() {
+ if( !this->kernel->config->value( spindle_module_enable_checksum )->by_default(false)->as_bool() ){ return; }
+
+ // Settings
+ this->on_config_reload(this);
+
+ this->register_for_event(ON_GCODE_EXECUTE);
+ this->register_for_event(ON_PLAY);
+ this->register_for_event(ON_PAUSE);
+ this->register_for_event(ON_BLOCK_BEGIN);
+ this->register_for_event(ON_BLOCK_END);
+}
+
+
+// Get config
+void Spindle::on_config_reload(void* argument){
+ this->spindle_pin= this->kernel->config->value(spindle_pin_checksum)->by_default("0.4" )->as_pin()->as_output();
+}
+
+// Turn spindle off spindle at the end of a move
+void Spindle::on_block_end(void* argument){
+ //this->spindle_pin->set(0);
+}
+
+// Set spindle power at the beginning of a block
+void Spindle::on_block_begin(void* argument){
+ //this->set_spindle();
+}
+
+// When the play/pause button is set to pause, or a module calls the ON_PAUSE event
+void Spindle::on_pause(void* argument){
+ this->spindle_pin->set(0);
+}
+
+// When the play/pause button is set to play, or a module calls the ON_PLAY event
+void Spindle::on_play(void* argument){
+ this->set_spindle();
+}
+
+// Turn spindle on/off depending on received GCodes
+void Spindle::on_gcode_execute(void* argument){
+ Gcode* gcode = static_cast<Gcode*>(argument);
+ this->spindle_on = false;
+ if( gcode->has_letter('G' )){
+ int code = gcode->get_value('G');
+ if( code == 0 ){ // G0
+ this->kernel->serial->printf("off\r\n");
+ this->spindle_pin->set(0);
+ this->spindle_on = false;
+ }else if( code >= 1 && code <= 3 ){ // G1, G2, G3
+ this->kernel->serial->printf("on\r\n");
+ this->spindle_pin->set(1);
+ this->spindle_on = true;
+ }
+ }
+}
+
+
+void Spindle::set_spindle(){
+ if( this->spindle_on && this->kernel->stepper->current_block ){
+ this->spindle_pin->set(1);
+ }
+}
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/spindle/Spindle.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/spindle/Spindle.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,51 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SPINDLE_MODULE_H
+#define SPINDLE_MODULE_H
+
+#include "libs/Module.h"
+#include "mbed.h"
+#include "libs/Kernel.h"
+#include "modules/communication/utils/Gcode.h"
+
+#define spindle_module_enable_checksum 43962
+#define spindle_pin_checksum 54937
+
+class Spindle : public Module{
+ public:
+ Spindle();
+ virtual void on_module_loaded();
+ virtual void on_config_reload(void* argument);
+ virtual void on_block_end(void* argument);
+ virtual void on_block_begin(void* argument);
+ virtual void on_play(void* argument);
+ virtual void on_pause(void* argument);
+ virtual void on_gcode_execute(void* argument);
+ private:
+ void set_spindle();
+ bool spindle_on; // Spindle status
+ Pin* spindle_pin;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/temperaturecontrol/TemperatureControl.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/temperaturecontrol/TemperatureControl.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,187 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// TODOÂ :Â THISÂ FILEÂ ISÂ LAME, MUSTÂ BEÂ MADEÂ MUCHÂ BETTER
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include <math.h>
+#include "TemperatureControl.h"
+#include "libs/Pin.h"
+
+TemperatureControl::TemperatureControl(){}
+
+TemperatureControl::TemperatureControl(uint16_t name){
+ this->name_checksum = name;
+ this->error_count = 0;
+}
+
+void TemperatureControl::on_module_loaded(){
+
+ // We start not desiring any temp
+ this->desired_adc_value = UNDEFINED;
+
+ // Settings
+ this->on_config_reload(this);
+
+ this->acceleration_factor = 10;
+
+ this->kernel->slow_ticker->attach( 20, this, &TemperatureControl::thermistor_read_tick );
+
+ // Register for events
+ this->register_for_event(ON_GCODE_EXECUTE);
+ this->register_for_event(ON_MAIN_LOOP);
+
+}
+
+void TemperatureControl::on_main_loop(void* argument){ }
+
+// Get configuration from the config file
+void TemperatureControl::on_config_reload(void* argument){
+
+ this->readings_per_second = this->kernel->config->value(temperature_control_checksum, this->name_checksum, readings_per_second_checksum)->by_default(5)->as_number();
+
+ // Values are here : http://reprap.org/wiki/Thermistor
+ this->r0 = 100000;
+ this->t0 = 25;
+ this->beta = 4066;
+ this->vadc = 3.3;
+ this->vcc = 3.3;
+ this->r1 = 0;
+ this->r2 = 4700;
+
+ // Preset values for various common types of thermistors
+ ConfigValue* thermistor = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_checksum);
+ if( thermistor->value.compare("EPCOS100K" ) == 0 ){ // Default
+ }else if( thermistor->value.compare("RRRF100K" ) == 0 ){ this->beta = 3960;
+ }else if( thermistor->value.compare("RRRF10K" ) == 0 ){ this->beta = 3964; this->r0 = 10000; this->r1 = 680; this->r2 = 1600;
+ }else if( thermistor->value.compare("Honeywell100K") == 0 ){ this->beta = 3974;
+ }else if( thermistor->value.compare("Semitec" ) == 0 ){ this->beta = 4267; }
+
+ // Preset values are overriden by specified values
+ this->r0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r0_checksum )->by_default(100000)->as_number(); // Stated resistance eg. 100K
+ this->t0 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, t0_checksum )->by_default(25 )->as_number() + 273.15; // Temperature at stated resistance, eg. 25C
+ this->beta = this->kernel->config->value(temperature_control_checksum, this->name_checksum, beta_checksum)->by_default(4066 )->as_number(); // Thermistor beta rating. See http://reprap.org/bin/view/Main/MeasuringThermistorBeta
+ this->vadc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vadc_checksum)->by_default(3.3 )->as_number(); // ADC Reference
+ this->vcc = this->kernel->config->value(temperature_control_checksum, this->name_checksum, vcc_checksum )->by_default(3.3 )->as_number(); // Supply voltage to potential divider
+ this->r1 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r1_checksum )->by_default(0 )->as_number();
+ this->r2 = this->kernel->config->value(temperature_control_checksum, this->name_checksum, r2_checksum )->by_default(4700 )->as_number();
+
+ // Thermistor math
+ this->k = this->r0 * exp( -this->beta / this->t0 );
+ if( r1 > 0 ){ this->vs = r1 * this->vcc / ( r1 + r2 ); this->rs = r1 * r2 / ( r1 + r2 ); }else{ this->vs = this->vcc; this->rs = r2; }
+
+ // Thermistor pin for ADC readings
+ this->thermistor_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, thermistor_pin_checksum )->required()->as_pin();
+ this->kernel->adc->enable_pin(this->thermistor_pin);
+
+ // Heater pin
+ this->heater_pin = this->kernel->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_pin()->as_output();
+ this->heater_pin->set(0);
+
+}
+
+void TemperatureControl::on_gcode_execute(void* argument){
+ Gcode* gcode = static_cast<Gcode*>(argument);
+
+ // Set temperature without waiting
+ if( gcode->has_letter('M') && gcode->get_value('M') == 104 && gcode->has_letter('S') ){
+ this->set_desired_temperature(gcode->get_value('S'));
+ }
+
+ // Set temperature and wait
+ if( gcode->has_letter('M') && gcode->get_value('M') == 109 && gcode->has_letter('S') ){
+ this->set_desired_temperature(gcode->get_value('S'));
+
+ // Pause
+ this->kernel->pauser->take();
+ this->waiting = true;
+
+ }
+
+ // Get temperature
+ if( gcode->has_letter('M') && gcode->get_value('M') == 105 ){
+ gcode->stream->printf("get temperature: %f current:%f target:%f \r\n", this->get_temperature(), this->new_thermistor_reading(), this->desired_adc_value );
+ }
+}
+
+void TemperatureControl::set_desired_temperature(double desired_temperature){
+ this->desired_adc_value = this->temperature_to_adc_value(desired_temperature);
+}
+
+double TemperatureControl::get_temperature(){
+ double temp = this->new_thermistor_reading() ;
+ return this->adc_value_to_temperature( this->new_thermistor_reading() );
+}
+
+double TemperatureControl::adc_value_to_temperature(double adc_value){
+ double v = adc_value * this->vadc; // Convert from 0-1 adc value to voltage
+ double r = this->rs * v / ( this->vs - v ); // Resistance of thermistor
+ return ( this->beta / log( r / this->k )) - 273.15;
+}
+
+double TemperatureControl::temperature_to_adc_value(double temperature){
+ double r = this->r0 * exp( this->beta * ( 1 / (temperature + 273.15) -1 / this->t0 ) ); // Resistance of the thermistor
+ double v = this->vs * r / ( this->rs + r ); // Voltage at the potential divider
+ return v / this->vadc * 1.00000; // The ADC reading
+}
+
+uint32_t TemperatureControl::thermistor_read_tick(uint32_t dummy){
+ if( this->desired_adc_value != UNDEFINED ){
+ if( this->new_thermistor_reading() > this->desired_adc_value ){
+ this->heater_pin->set(1);
+ }else{
+ this->heater_pin->set(0);
+ if( this->waiting ){
+ this->kernel->pauser->release();
+ this->waiting = false;
+ }
+ }
+ }
+}
+
+double TemperatureControl::new_thermistor_reading(){
+
+ double new_reading = double( double(this->kernel->adc->read(this->thermistor_pin) / double(1<<12) ) );
+
+ if( this->queue.size() < 15 ){
+ this->queue.push_back( new_reading );
+ return new_reading;
+ }else{
+ double current_temp = this->average_adc_reading();
+ double error = fabs(new_reading - current_temp);
+ if( error < 0.1 ){
+ this->error_count = 0;
+ double test;
+ this->queue.pop_front(test);
+ this->queue.push_back( new_reading );
+ }else{
+ this->error_count++;
+ if( this->error_count > 4 ){
+ double test;
+ this->queue.pop_front(test);
+ }
+ }
+ return current_temp;
+ }
+}
+
+
+double TemperatureControl::average_adc_reading(){
+ double total;
+ int j=0;
+ int reading_index = this->queue.head;
+ while( reading_index != this->queue.tail ){
+ j++;
+ total += this->queue.buffer[reading_index];
+ reading_index = this->queue.next_block_index( reading_index );
+ }
+ return total / j;
+}
+
+
+
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/temperaturecontrol/TemperatureControl.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/temperaturecontrol/TemperatureControl.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,77 @@
+/*
+ this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl).
+ smoothie 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.
+ smoothie 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 smoothie. if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef temperaturecontrol_h
+#define temperaturecontrol_h
+
+#include "libs/Pin.h"
+#include <math.h>
+
+#define UNDEFINED -1
+
+#define thermistor_checksum 41045
+#define r0_checksum 5538
+#define readings_per_second_checksum 18645
+#define t0_checksum 6564
+#define beta_checksum 1181
+#define vadc_checksum 10911
+#define vcc_checksum 36157
+#define r1_checksum 5795
+#define r2_checksum 6052
+#define temperature_control_checksum 44054
+#define thermistor_pin_checksum 1788
+#define heater_pin_checksum 35619
+
+class TemperatureControl : public Module {
+ public:
+ TemperatureControl();
+ TemperatureControl(uint16_t name);
+
+ virtual void on_module_loaded();
+ virtual void on_main_loop(void* argument);
+ virtual void on_gcode_execute(void* argument);
+ virtual void on_config_reload(void* argument);
+ void set_desired_temperature(double desired_temperature);
+ double get_temperature();
+ double adc_value_to_temperature(double adc_value);
+ double temperature_to_adc_value(double temperature);
+ uint32_t thermistor_read_tick(uint32_t dummy);
+ double new_thermistor_reading();
+ double average_adc_reading();
+
+ double desired_adc_value;
+ double tail_adc_value;
+ double head_adc_value;
+
+ // Thermistor computation settings
+ double r0;
+ double t0;
+ double r1;
+ double r2;
+ double beta;
+ double vadc;
+ double vcc;
+ double k;
+ double vs;
+ double rs;
+
+ double acceleration_factor;
+ double readings_per_second;
+
+ RingBuffer<double,16> queue; // Queue of Blocks
+ int error_count;
+
+ uint16_t name_checksum;
+
+ Pin* thermistor_pin;
+ Pin* heater_pin;
+
+ bool waiting;
+
+};
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/temperaturecontrol/TemperatureControlPool.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/temperaturecontrol/TemperatureControlPool.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,37 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Module.h"
+#include "libs/Kernel.h"
+#include <math.h>
+using namespace std;
+#include <vector>
+#include "TemperatureControlPool.h"
+#include "TemperatureControl.h"
+
+TemperatureControlPool::TemperatureControlPool(){}
+
+void TemperatureControlPool::on_module_loaded(){
+
+ vector<uint16_t> modules;
+ this->kernel->config->get_module_list( &modules, temperature_control_checksum );
+
+ for( int i = 0; i < modules.size(); i++ ){
+ // If module is enabled
+ if( this->kernel->config->value(temperature_control_checksum, modules[i], enable_checksum )->as_bool() == true ){
+ TemperatureControl* controller = new TemperatureControl(modules[i]);
+ this->kernel->add_module(controller);
+ this->controllers.push_back( controller );
+ }
+ }
+
+}
+
+
+
+
+
diff -r 000000000000 -r 31e91bb0ef3c modules/tools/temperaturecontrol/TemperatureControlPool.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/tools/temperaturecontrol/TemperatureControlPool.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,30 @@
+/*
+ this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl).
+ smoothie 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.
+ smoothie 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 smoothie. if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TEMPERATURECONTROLPOOL_H
+#define TEMPERATURECONTROLPOOL_H
+
+#include "TemperatureControl.h"
+#include <math.h>
+using namespace std;
+#include <vector>
+
+#define temperature_control_checksum 44054
+#define enable_checksum 29545
+
+class TemperatureControlPool : public Module {
+ public:
+ TemperatureControlPool();
+
+ virtual void on_module_loaded();
+
+ vector<TemperatureControl*> controllers;
+};
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/configurator/Configurator.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/configurator/Configurator.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,128 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "libs/Kernel.h"
+#include "Configurator.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/SerialMessage.h"
+#include "libs/StreamOutput.h"
+
+
+void Configurator::on_module_loaded(){
+ this->register_for_event(ON_CONSOLE_LINE_RECEIVED);
+// this->register_for_event(ON_GCODE_EXECUTE);
+// this->register_for_event(ON_MAIN_LOOP);
+}
+
+// When a new line is received, check if it is a command, and if it is, act upon it
+void Configurator::on_console_line_received( void* argument ){
+ SerialMessage new_message = *static_cast<SerialMessage*>(argument);
+ string possible_command = new_message.message;
+
+ // We don't compare to a string but to a checksum of that string, this saves some space in flash memory
+ uint16_t check_sum = get_checksum( possible_command.substr(0,possible_command.find_first_of(" \r\n")) ); // todo:Â put this method somewhere more convenient
+
+ // Act depending on command
+ switch( check_sum ){
+ case config_get_command_checksum: this->config_get_command( get_arguments(possible_command), new_message.stream ); break;
+ case config_set_command_checksum: this->config_set_command( get_arguments(possible_command), new_message.stream ); break;
+ case config_load_command_checksum: this->config_load_command( get_arguments(possible_command), new_message.stream ); break;
+ }
+}
+
+// Process and respond to eeprom gcodes (M50x)
+void Configurator::on_gcode_execute(void* argument){
+ Gcode* gcode = static_cast<Gcode*>(argument);
+ if( gcode->has_letter('G') ){
+ int code = gcode->get_value('G');
+ switch( code ){
+ }
+ }
+ else if( gcode->has_letter('M') ){
+ int code = gcode->get_value('M');
+ switch( code ){
+ }
+ }
+}
+
+void Configurator::on_main_loop(void* argument){}
+
+// Output a ConfigValue from the specified ConfigSource to the stream
+void Configurator::config_get_command( string parameters, StreamOutput* stream ){
+ string source = shift_parameter(parameters);
+ string setting = shift_parameter(parameters);
+ if (setting == "") { // output live setting
+ setting = source;
+ source = "";
+ vector<uint16_t> setting_checksums = get_checksums( setting );
+ ConfigValue* cv = this->kernel->config->value(setting_checksums);
+ string value = "";
+ if(cv->found){ value = cv->as_string(); }
+ stream->printf( "live: %s is set to %s\r\n", setting.c_str(), value.c_str() );
+ } else { // output setting from specified source
+ uint16_t source_checksum = get_checksum( source );
+ vector<uint16_t> setting_checksums = get_checksums( setting );
+ for(int i=0; i < this->kernel->config->config_sources.size(); i++){
+ if( this->kernel->config->config_sources[i]->is_named(source_checksum) ){
+ string value = this->kernel->config->config_sources[i]->read(setting_checksums);
+ stream->printf( "%s: %s is set to %s\r\n", source.c_str(), setting.c_str(), value.c_str() );
+ break;
+ }
+ }
+ }
+}
+
+// Write the specified setting to the specified ConfigSource
+void Configurator::config_set_command( string parameters, StreamOutput* stream ){
+ string source = shift_parameter(parameters);
+ string setting = shift_parameter(parameters);
+ string value = shift_parameter(parameters);
+ if (value == "") {
+ value = setting;
+ setting = source;
+ source = "";
+ this->kernel->config->set_string(setting, value);
+ stream->printf( "live: %s has been set to %s\r\n", setting.c_str(), value.c_str() );
+ } else {
+ uint16_t source_checksum = get_checksum(source);
+ for(int i=0; i < this->kernel->config->config_sources.size(); i++){
+ if( this->kernel->config->config_sources[i]->is_named(source_checksum) ){
+ this->kernel->config->config_sources[i]->write(setting, value);
+ stream->printf( "%s: %s has been set to %s\r\n", source.c_str(), setting.c_str(), value.c_str() );
+ break;
+ }
+ }
+ }
+}
+
+// Reload config values from the specified ConfigSource
+void Configurator::config_load_command( string parameters, StreamOutput* stream ){
+ string source = shift_parameter(parameters);
+ if(source == ""){
+ this->kernel->config->config_cache_load();
+ this->kernel->call_event(ON_CONFIG_RELOAD);
+ stream->printf( "Reloaded settings\r\n" );
+ } else if(file_exists(source)){
+ FileConfigSource fcs(source);
+ fcs.transfer_values_to_cache(&this->kernel->config->config_cache);
+ this->kernel->call_event(ON_CONFIG_RELOAD);
+ stream->printf( "Loaded settings from %s\r\n", source.c_str() );
+ } else {
+ uint16_t source_checksum = get_checksum(source);
+ for(int i=0; i < this->kernel->config->config_sources.size(); i++){
+ if( this->kernel->config->config_sources[i]->is_named(source_checksum) ){
+ this->kernel->config->config_sources[i]->transfer_values_to_cache(&this->kernel->config->config_cache);
+ this->kernel->call_event(ON_CONFIG_RELOAD);
+ stream->printf( "Loaded settings from %s\r\n", source.c_str() );
+ break;
+ }
+ }
+ }
+}
+
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/configurator/Configurator.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/configurator/Configurator.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,42 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef configurator_h
+#define configurator_h
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/StreamOutput.h"
+
+
+#define CONF_NONE 0
+#define CONF_ROM 1
+#define CONF_SD 2
+#define CONF_EEPROM 3
+
+#define config_get_command_checksum 46310 // "config-get"
+#define config_set_command_checksum 55538 // "config-set"
+#define config_load_command_checksum 3143 // "config-load"
+
+class Configurator : public Module {
+ public:
+ Configurator(){}
+
+ virtual void on_module_loaded();
+ virtual void on_console_line_received( void* argument );
+ virtual void on_gcode_execute( void* argument );
+ virtual void on_main_loop( void* argument );
+
+ void config_get_command( string parameters, StreamOutput* stream );
+ void config_set_command( string parameters, StreamOutput* stream );
+ void config_load_command(string parameters, StreamOutput* stream );
+};
+
+
+#endif // configurator_h
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/currentcontrol/CurrentControl.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/currentcontrol/CurrentControl.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,26 @@
+#include "libs/Kernel.h"
+#include "CurrentControl.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+using namespace std;
+
+CurrentControl::CurrentControl(){}
+
+void CurrentControl::on_module_loaded(){
+ if( !this->kernel->config->value( currentcontrol_module_enable_checksum )->by_default(false)->as_bool() ){ return; }
+
+ // Get configuration
+ this->alpha_current = this->kernel->config->value(alpha_current_checksum )->by_default(0.8)->as_number();
+ this->beta_current = this->kernel->config->value(beta_current_checksum )->by_default(0.8)->as_number();
+ this->gamma_current = this->kernel->config->value(gamma_current_checksum )->by_default(0.8)->as_number();
+ this->delta_current = this->kernel->config->value(delta_current_checksum )->by_default(0.8)->as_number();
+
+ this->kernel->digipot->set_current(0, this->alpha_current);
+ this->kernel->digipot->set_current(1, this->beta_current );
+ this->kernel->digipot->set_current(2, this->gamma_current);
+ this->kernel->digipot->set_current(3, this->delta_current);
+
+}
+
+
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/currentcontrol/CurrentControl.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/currentcontrol/CurrentControl.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,31 @@
+#ifndef CURRENTCONTROL_H
+#define CURRENTCONTROL_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+
+#define alpha_current_checksum 22381
+#define beta_current_checksum 60163
+#define gamma_current_checksum 12906
+#define delta_current_checksum 30321
+#define currentcontrol_module_enable_checksum 38842
+
+class CurrentControl : public Module {
+ public:
+ CurrentControl();
+
+ virtual void on_module_loaded();
+
+ double alpha_current;
+ double beta_current;
+ double gamma_current;
+ double delta_current;
+};
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/pausebutton/PauseButton.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/pausebutton/PauseButton.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,48 @@
+#include "libs/Kernel.h"
+#include "PauseButton.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include <string>
+using namespace std;
+
+PauseButton::PauseButton(){}
+
+void PauseButton::on_module_loaded(){
+ this->button_state = true;
+ this->play_state = true;
+ this->register_for_event(ON_PLAY);
+ this->register_for_event(ON_PAUSE);
+
+ this->button = this->kernel->config->value( pause_button_pin_checksum )->by_default("2.12")->as_pin()->as_input();
+ this->led = this->kernel->config->value( pause_led_pin_checksum )->by_default("4.28")->as_pin()->as_output();
+
+ this->kernel->slow_ticker->attach( 100, this, &PauseButton::button_tick );
+}
+
+//TODO:Â Make this use InterruptIn
+//Check the state of the button and act accordingly
+uint32_t PauseButton::button_tick(uint32_t dummy){
+ // If button changed
+ if(this->button_state != this->button->get()){
+ this->button_state = this->button->get();
+ // If button pressed
+ if( this->button_state ){
+ if( this->play_state ){
+ this->play_state = false;
+ this->kernel->pauser->take();
+ }else{
+ this->play_state = true;
+ this->kernel->pauser->release();
+ }
+ }
+ }
+}
+
+void PauseButton::on_play( void* argument ){
+ this->led->set(0);
+}
+
+void PauseButton::on_pause( void* argument ){
+ this->led->set(1);
+}
+
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/pausebutton/PauseButton.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/pausebutton/PauseButton.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,37 @@
+#ifndef PAUSEBUTTON_H
+#define PAUSEBUTTON_H
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/Pin.h"
+
+#define pause_button_pin_checksum 32709
+#define pause_led_pin_checksum 48477
+
+class PauseButton : public Module {
+ public:
+ PauseButton();
+
+ virtual void on_module_loaded();
+ uint32_t button_tick(uint32_t dummy);
+ virtual void on_play( void* argument );
+ virtual void on_pause( void* argument );
+
+ Pin* button;
+ Pin* led;
+ bool button_state;
+ bool play_state;
+};
+
+
+
+
+
+
+
+
+
+
+
+#endif
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/simpleshell/SimpleShell.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/simpleshell/SimpleShell.cpp Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,162 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "libs/Kernel.h"
+#include "SimpleShell.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/SerialMessage.h"
+#include "libs/StreamOutput.h"
+#include "modules/robot/Player.h"
+
+
+void SimpleShell::on_module_loaded(){
+ this->current_path = "/";
+ this->playing_file = false;
+ this->register_for_event(ON_CONSOLE_LINE_RECEIVED);
+ this->register_for_event(ON_MAIN_LOOP);
+}
+
+// When a new line is received, check if it is a command, and if it is, act upon it
+void SimpleShell::on_console_line_received( void* argument ){
+ SerialMessage new_message = *static_cast<SerialMessage*>(argument);
+ string possible_command = new_message.message;
+
+ // We don't compare to a string but to a checksum of that string, this saves some space in flash memory
+ unsigned short check_sum = get_checksum( possible_command.substr(0,possible_command.find_first_of(" \r\n")) ); // todo:Â put this method somewhere more convenient
+
+ // Act depending on command
+ switch( check_sum ){
+ case ls_command_checksum : this->ls_command( get_arguments(possible_command), new_message.stream ); break;
+ case cd_command_checksum : this->cd_command( get_arguments(possible_command), new_message.stream ); break;
+ case pwd_command_checksum : this->pwd_command( get_arguments(possible_command), new_message.stream ); break;
+ case cat_command_checksum : this->cat_command( get_arguments(possible_command), new_message.stream ); break;
+ case play_command_checksum : this->play_command(get_arguments(possible_command), new_message.stream ); break;
+ case reset_command_checksum : this->reset_command(get_arguments(possible_command),new_message.stream ); break;
+ }
+}
+
+// Convert a path indication ( absolute or relative ) into a path ( absolute )
+string SimpleShell::absolute_from_relative( string path ){
+ if( path[0] == '/' ){ return path; }
+ if( path[0] == '.' ){ return this->current_path; }
+ return this->current_path + path;
+}
+
+// Act upon an ls command
+// Convert the first parameter into an absolute path, then list the files in that path
+void SimpleShell::ls_command( string parameters, StreamOutput* stream ){
+ string folder = this->absolute_from_relative( parameters );
+ DIR* d;
+ struct dirent* p;
+ d = opendir(folder.c_str());
+ if(d != NULL) {
+ while((p = readdir(d)) != NULL) { stream->printf("%s\r\n", lc(string(p->d_name)).c_str()); }
+ } else {
+ stream->printf("Could not open directory %s \r\n", folder.c_str());
+ }
+}
+
+// Change current absolute path to provided path
+void SimpleShell::cd_command( string parameters, StreamOutput* stream ){
+ string folder = this->absolute_from_relative( parameters );
+ if( folder[folder.length()-1] != '/' ){ folder += "/"; }
+ DIR *d;
+ struct dirent *p;
+ d = opendir(folder.c_str());
+ if(d == NULL) {
+ stream->printf("Could not open directory %s \r\n", folder.c_str() );
+ }else{
+ this->current_path = folder;
+ }
+}
+
+// Responds with the present working directory
+void SimpleShell::pwd_command( string parameters, StreamOutput* stream ){
+ stream->printf("%s\r\n", this->current_path.c_str());
+}
+
+// Output the contents of a file, first parameter is the filename, second is the limit ( in number of lines to output )
+void SimpleShell::cat_command( string parameters, StreamOutput* stream ){
+
+ // Get parameters ( filename and line limit )
+ string filename = this->absolute_from_relative(shift_parameter( parameters ));
+ string limit_paramater = shift_parameter( parameters );
+ int limit = -1;
+ if( limit_paramater != "" ){ limit = int(atof(limit_paramater.c_str())); }
+
+ // Open file
+ FILE *lp = fopen(filename.c_str(), "r");
+ if(lp == NULL) {
+ stream->printf("File not found: %s\r\n", filename.c_str());
+ return;
+ }
+ string buffer;
+ int c;
+ int newlines = 0;
+
+ // Print each line of the file
+ while ((c = fgetc (lp)) != EOF){
+ buffer.append((char *)&c, 1);
+ if( char(c) == '\n' ){
+ newlines++;
+ stream->printf("%s", buffer.c_str());
+ buffer.clear();
+ }
+ if( newlines == limit ){ break; }
+ };
+ fclose(lp);
+
+}
+
+// Play a gcode file by considering each line as if it was received on the serial console
+void SimpleShell::play_command( string parameters, StreamOutput* stream ){
+ // Get filename
+ string filename = this->absolute_from_relative(shift_parameter( parameters ));
+ this->current_file_handler = fopen( filename.c_str(), "r");
+ if(this->current_file_handler == NULL)
+ {
+ stream->printf("File not found: %s\r\n", filename.c_str());
+ return;
+ }
+ this->playing_file = true;
+ this->current_stream = stream;
+}
+
+// Reset the system
+void SimpleShell::reset_command( string parameters, StreamOutput* stream){
+ stream->printf("Smoothie out. Peace.\r\n");
+ system_reset();
+}
+
+void SimpleShell::on_main_loop(void* argument){
+
+ if( this->playing_file ){
+ string buffer;
+ int c;
+ // Print each line of the file
+ while ((c = fgetc(this->current_file_handler)) != EOF){
+ if (c == '\n'){
+ this->current_stream->printf("%s\n", buffer.c_str());
+ struct SerialMessage message;
+ message.message = buffer;
+ message.stream = this->current_stream;
+ // wait for the queue to have enough room that a serial message could still be received before sending
+ this->kernel->player->wait_for_queue(2);
+ this->kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message);
+ buffer.clear();
+ return;
+ }else{
+ buffer += c;
+ }
+ };
+
+ fclose(this->current_file_handler);
+ this->playing_file = false;
+ }
+}
diff -r 000000000000 -r 31e91bb0ef3c modules/utils/simpleshell/SimpleShell.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/utils/simpleshell/SimpleShell.h Tue Jul 31 21:11:18 2012 +0000
@@ -0,0 +1,47 @@
+/*
+ This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+ Smoothie 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.
+ Smoothie 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 Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef simpleshell_h
+#define simpleshell_h
+
+#include "libs/Kernel.h"
+#include "libs/nuts_bolts.h"
+#include "libs/utils.h"
+#include "libs/StreamOutput.h"
+
+
+#define ls_command_checksum 19679
+#define cd_command_checksum 11207
+#define pwd_command_checksum 42060
+#define cat_command_checksum 24889
+#define play_command_checksum 17335
+#define reset_command_checksum 27429
+
+class SimpleShell : public Module {
+ public:
+ SimpleShell(){}
+
+ virtual void on_module_loaded();
+ virtual void on_console_line_received( void* argument );
+ virtual void on_main_loop( void* argument );
+ string absolute_from_relative( string path );
+ void ls_command( string parameters, StreamOutput* stream );
+ void cd_command( string parameters, StreamOutput* stream );
+ void pwd_command( string parameters, StreamOutput* stream );
+ void cat_command( string parameters, StreamOutput* stream );
+ void play_command( string parameters, StreamOutput* stream );
+ void reset_command(string parameters, StreamOutput* stream );
+
+ string current_path;
+ bool playing_file;
+ StreamOutput* current_stream;
+ FILE* current_file_handler;
+};
+
+
+#endif