EPG Testing sequential pulsar using the Arduino

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #25, on April 30th, 2011, 08:42 PM »
Quote
note: after copy remove the spaces in the include statements.
witch ones do i need remove?

this is amazing...

your work is much appreciated!

and if you can get a simple GUI to interface with it... this would be so simple for anyone to use! :) nice!

i still need to hook it up and play with it! then i will see whats next!

what size Pots are you using?

also would love to get a live help on skype once i get it hooked up!

Skype: rwg42985

thanks! ~Russ[/quote]

Forum Administrator

RE: EPG Testing sequential pulsar using the Arduino
« Reply #26, on April 30th, 2011, 10:13 PM »
Nice Work everyone, absolutely amazing.

BTW if you click the little guy with the text box beside the find button, you can add rep for good posts. . .not sure if anyone cares, but I'm giving out reps for everyone in this thread.

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #27, on May 1st, 2011, 11:31 AM »
txqNL, one more small request,

If there a way with out redoing the hole thing...

A duty cycle of over 100%, so we have phasing, the LEDs will overlap each other?

Up to 150% atleast. Up to 199% would be better...

Mabbye a sub program tonmake this work? Can be in a derfent mode?

What do you think? Eazy or will it be a total re build of the code?

Your skills are freekin sweet sir!

Also, to make a GUI... It would just read and write from a serial connection? So just read and wright commands? That should be eazy yeah?

Thanks for the hard work!!! I would be know where near where we are right now!:)    

Thanks,

~Russ

txqNL

RE: EPG Testing sequential pulsar using the Arduino
« Reply #28, on May 1st, 2011, 04:33 PM »Last edited on May 1st, 2011, 04:50 PM by txqNL
Quote from Rwg42985
witch ones do i need remove?
Currently the 10 extra spaces in the following 5 lines, because the forum else removes the text.
Code: [Select]

#include < avr/io.h >
#include < avr/interrupt.h >
#include < avr/pgmspace.h >
#include < util/delay.h >
#include < LiquidCrystal.h >
Quote from Rwg42985 on May 1st, 2011, 11:31 AM
A duty cycle of over 100%, so we have phasing, the LEDs will overlap each other?
Up to 150% atleast. Up to 199% would be better...
Mabbye a sub program tonmake this work? Can be in a derfent mode?
What do you think? Eazy or will it be a total re build of the code?
Your skills are freekin sweet sir!
Also, to make a GUI... It would just read and write from a serial connection? So just read and wright commands? That should be eazy yeah?
Indeed gui would get/set commands and with the calc variables the gui pots/sliders have the correct ranges so it should be less code then the pulse fire code itself, but the gui is very last on my list.

The pulse fire code will work with only the outputs connected all other connections are OPTIONAL !!
The pots I use are 100k but are dividers between 5v and ground so any value would work correct, but would not go below ~5k because else current in pot will get too large.

I've checked the code rawbush posted and think I can add a request freq command what will set multiple variables in one command.
(and realized that it is c++ code in arduino ide, so code can be clean a bit more)
But first thing is to get the variables saved so we can setup the chip
with some commands and use it standalone in different applications.
For example I need 3 output for classic plate cell in my car where the 2 analog inputs maybe connect to engine temp for soft start and the other to gas valve or the vacuum sensor which is the ~inverse.
For tube cell I need 1,2,3 or 6 output, etc,etc.
So I want it to be so universal as it can be as a sort of pwm.

For the overlapping of outputs there need to be new pulse mode.
In the pwm code for 1 output in my autolpm code I used bit fields to sumulate duty and bursting by shifting it to the output bit, like this;
Code: [Select]

uint16_t ppm_seq_type_data[25] = {

0b1000000000000000, // 93.75%  - 1
0b1100000000000000,
0b1110000000000000,
0b1111000000000000,
0b1111100000000000,
0b1111110000000000,
0b1111111000000000,
0b1111111100000000, // 50% - 7
0b1111111110000000,
0b1111111111000000,
0b1111111111100000,
0b1111111111110000,
0b1111111111111000,
0b1111111111111100,
0b1111111111111110, // 6.25%  - 14
 
0b1010101010101010, // turbe mode 50 %
0b1010101011111111,
0b1010101111111111,

0b1001001111111111, //
0b1001001001111111,
0b1001001001001111,
0b1001001001001001,

0b1000111111111111, //
0b1000011100110111,
0b1000001111100011
};


I think this concept can also be done for multiple outputs where every output is one step state ahead.
But it will make freq calc more difficult, I currently find it already hard to get all the options in the calculation which influence the output freq.
Also there still needs to be some changed to the code for steping because i also want have good resolution on higher speeds.
 
To have a led overlap of 100% you can do that already, start typing;
pulse_init 3      (Start with 2 output on)
pulse_steps 64 (because higher init value steps are bigger)
But when enabling other options the cycle of the first 2 outputs will alway be the same.

Try this;
pulse_init 1      (back to start with one output)
pulse_steps 16 (back to 5 steps/outputs)
pulse_duty 10   (Add wait after train now 10*train_loop to wait before starting next pulse train)
train_loop 5000 (increase speed so now wait 5000x for doing next step)
train_inc 200    (lower train_loop value per step, so nice max value is train_loop(5000)/number_of_outputs(5) = 1000)
step_loop 2000 (enable off time after step, so wait 2000x step int before forint next step)
step_inc 150    (make off time less per step again nice max is step_loop/steps)

So after adjusting x_loop you often also adjust x_inc.
For x_clock and x_tcnt it is more complicated; x_clock determents the divider of the cpu clock (16Mhz) to the hardware counter currently 0.
And the _tcnt is the inital value of the hardware counter which we use to divider the clock by 16. So setting the clock to divider by 64 the tcnt value should also be change to reflect the correct base freq which is fed to the (wait) x_loop variable.

I guessing magnagas clusters into N/S and travel like pulses in the tube.
Then you can get a bit of voltage when coil is around tube, so to add triggering coil just in front of power coil, to do this do something like;
Add trigger coil to ground and other side to diode which goes into comparator IC other side comparator is connected to pot which is in between Vcc and ground.
Output is fed to pin D2 and selected via "pulse_trigger 2".
(maybe the first small winding on your core can function as trigger coil)

Hope be to online somewhere next week for skype, until then lets get good results.

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #29, on May 1st, 2011, 05:37 PM »
Quote from txqNL on May 1st, 2011, 04:33 PM
Quote from Rwg42985
witch ones do i need remove?
Quote
Currently the 10 extra spaces in the following 5 lines, because the forum else removes the text.
ill see what we can do on this... that is strange... the forms still have small bugs witch we are trying to work out...


i'm getting my setup ready for testing i will set it up and play with your code which i love!   i will get to play with this and understand it better!

also if we just have a push button interface with the LCD display that would be cool!

this way we can do away with the pc...??? that can be later... but that would make it easy to use with no pc.

thanks again for your efforts! we all have our skills and together we can make some nice stuff that works! and we all can learn!

Thanks you!

~Russ

txqNL

RE: EPG Testing sequential pulsar using the Arduino
« Reply #30, on May 5th, 2011, 06:00 PM »Last edited on May 21st, 2011, 04:41 PM by txqNL
Quote from Rwg42985 on May 1st, 2011, 05:37 PM
also if we just have a push button interface with the LCD display that would be cool!
this way we can do away with the pc...??? that can be later... but that would make it easy to use with no pc.
new version;
Code: [Select]

Lastest code somewhere in thread, see attachments

note: remove spaces from include statements after copy from forum.
note2: Also added project as attachment because I get the above code not correct copy pasted.

Changed a lot;
- serial speed 115200
- config can be saved in eeprom
- soft startup via extra off duty
- mappable analog input to variable with range
- 'WIP' code for pulse seqence with offset into next step
- simple lcd ui via 2 button:
  lcd_menu_off; push0=enter menu, push1=manual trigger
  lcd_menu_sel; push0=next items,  push1=change value
  lcd_menu_val; push0=+ , push1=- , both is *10

Now about 30 settings (and 15 in lcd)
Code: [Select]

root@pulsefire: info
sys_lcd_time==200
sys_menu_time==4000
sys_adc_time==170
sys_adc_map0==0 0 0 0
sys_adc_map1==1 0 0 0
sys_adc_map2==2 0 0 0
sys_adc_map3==3 0 0 0
sys_adc_map4==4 1 30 300
sys_adc_map5==5 12 0 15
sys_input_time==5
sys_input_delay==300
sys_start_secs==0
sys_start_duty==50
pulse_steps==5
pulse_duty==0
pulse_trigger==0
pulse_mode==4
pulse_init==1
pulse_seq_off==6
pulse_seq_type==0
pulse_seq_data==65535
pulse_seq_len==15
train_loop==213
train_inc==0
train_tcnt==65376
train_clock==1
step_loop==0
step_inc==0
step_tcnt==96
step_clock==1
pulse_data==1
train_loop_cnt==140
train_state==1
step_loop_cnt==0
step_state==0
calc_train_speed==100000
calc_train_loop==9
calc_train_freq==0
calc_step_speed==100000
calc_step_loop==0
calc_step_freq_on==4
calc_step_freq_off==0
info_up==409868
info_cpu==16000000
info==done
root@pulsefire:


The phase shifting is now almost in via the seqence and offset but not fully correct yet, next todo request/calc freq and validate input.
And wanna check 8 bit or more output mode.
Is 8 outputs enough for push / pull setups or run 2 pulse fires ?

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #31, on May 5th, 2011, 06:47 PM »Last edited on May 5th, 2011, 06:49 PM by ~Russ/Rwg42985
Quote from txqNL on May 5th, 2011, 06:00 PM
Quote from Rwg42985 on May 1st, 2011, 05:37 PM
also if we just have a push button interface with the LCD display that would be cool!
this way we can do away with the pc...??? that can be later... but that would make it easy to use with no pc.
new version;
Code: [Select]
/*

pulse-fire - Automatic Pulse Fire Seqence Generator.
Created 26-Apr-2011 by Willem Cazander
All source is copyfree for all.

Arduino Hardware:
D0/D1  = Rx/Tx Serial console (is mapped to usb on Arduino)
D2     = Interupt hardware trigger for pulse train via hall sensor (pulse_trigger=2)
D3     = Input0 Push button to the ground for menu and add
D4     = Input1 Push button to the ground for trigger/select/subtract
D5     = Interups hardware for timer clock (pulse_clock=6or7)
D6     = LCD RS
D7     = LCD E
D8-D13 = Max 6 outputs to LED(+R) to ground.
A0-A3  = LCD DATA
A4     = Analog input4
A5     = Analog input5
(note: at least one output is needed all the other I/O is optional.)

Serial user interface;
(note: setup 115200b with Newline on enter/return)

root@pulsefire: help
# The commands are;
help               - Shows this
info               - Shows all possible information
save               - Save config value into eeprom
version            - Print the version for parsers
defaults           - Reverts the config values to defaults. (does not save!)
sys_lcd_time       - ms delay before next refresh
sys_menu_time      - ms idle before exit menu
sys_adc_time       - ms delay before next analog readout
sys_adc_map        - Maps analog input to variables with range
sys_input_time     - ms delay before next push button readout
sys_input_delay    - ms delay after push button pressed
sys_start_secs     - Soft start in seconds
sys_start_duty     - Soft start begin duty (should always be bigger then pulse_duty)
pulse_steps        - Number of outputs
pulse_duty         - Off duty after pulse train
pulse_trigger      - Pulse train trigger, 0=loop,1=Input1,2=extern)
pulse_mode         - Pulse train mode, 0=off,1=flash,2=train(def),3=reverse,4=seqence
pulse_init         - Start data for ouput train pulse shifting
pulse_seq_off      - Seqence offset between next step
pulse_seq_type     - Seqence data type,0=pulse_seq_data,1-20 interal pulse seqences
pulse_seq_data     - Custom seqence data 0-65535
pulse_seq_len      - Seqence data length max 15
train_loop         - 16 bit, loop counter of interrupts from clock of timer.
train_inc          - Auto increase speed of train_loop nice max is 1/6 of train_loop
train_tcnt         - Start value of train timer counter (16bit)
train_clock        - Clock prescaler for train timer
step_loop          - Off duty time between train steps, via seperate timer
step_inc           - Auto increase speed of train_loop nice max is 1/6 of step_loop
step_tcnt          - Start value of off duty timer counter (8bit)
step_clock         - Clock prescaler for off duty timer

Allmost all commands have getter and setter interface like this;

root@pulsefire: train_loop
train_wait==2345
root@pulsefire: train_loop 2000
train_wait=2000

root@pulsefire: pulse_trigger 1
pulse_trigger=1
Trigger determents what triggers the next pulse train.
0 - Continues (default)
1 - Manual trigger via button
2 - External trigger on edge

root@pulsefire: pulse_mode 0
pulse_mode=0
The pulse mode it the output type
0 - Off, so no output
1 - Flashing
2 - Pulse train (default)
3 - Pulse train reverse
4 - Seqenceing

root@pulsefire: sys_adc_map 4 1 30 300
Maps analog input4 to train_loop with range from 30 to 300.

root@pulsefire: train_tcnt
train_tcnt=65520
16bit Timer counter start index after overflow default set to 65520 for 1Mhz clock values;
The four nice 10x numbers are;
65520 = 16000000/(65536-(65536-16)) = 1000000 = 1Mhz
65376 = 16000000/(65536-(65536-160)) = 100000 = 100Khz
63936 = 16000000/(65536-(65536-1600)) = 10000 = 10Khz
49536 = 16000000/(65536-(65536-16000)) = 1000 = 1Khz

root@pulsefire: train_clock 2
train_clock=2
Clock values 0-7 default to 1;
CS12 CS11 CS10 Description
0 0 0 No clock source (Timer/Counter stopped).
0 0 1 clkI/O/1 (No prescaling)
0 1 0 clkI/O/8 (From prescaler)
0 1 1 clkI/O/64 (From prescaler)
1 0 0 clkI/O/256 (From prescaler)
1 0 1 clkI/O/1024 (From prescaler)
1 1 0 External clock source on T1 pin. Clock on falling edge.
1 1 1 External clock source on T1 pin. Clock on rising edge.
note: T1 is connected to D5


ASCII ART TABLE NEED MORE..

T1 -
T2 -
T3 -
T4 -


  +---------------T4--------------+    
  +----+                          +----+
D0|    |                          |    |
  |    |                          |    |
--+    +--------------------------+    +---
  +-T1-+-T2-+    
            +----+
Dx          |    |
            |    |
------------+    +-------------------------
                         +---T3---+
                    +----+
Dlast               |    |
                    |    |
--------------------+    +-----------------

TODO LIST:
- fix bugs
- change pulse_duty to percentage of train_loop
- add input validation
- inverse output
- request_freq command
- finalize speed freq calculations
- A step cycle of over 100%, so we have phasing, the LEDs will overlap each other?
- Hardware simple and advanced outputs (pin) layout finalized.
- PC GUI

*/

#include < avr/io.h >
#include < avr/eeprom.h >
#include < avr/interrupt.h >
#include < avr/pgmspace.h >
#include < util/delay.h >
#include < LiquidCrystal.h >

// Global data
#define ZERO                     0
#define ONE                      1
#define PMCMDLIST_SIZE           30   // array size of commands.
#define PMLCDLIST_SIZE           15   // array size of lcd items.
#define CMD_BUFF_SIZE            30   // max cmd length
#define UNPSTR_BUFF_SIZE         80   // max string lenght
#define ADC_VALUE_MAX            1023 // 10bit adc

// PulseFire data
#define PULSE_FIRE_VERSION       04
#define PULSE_SERIAL_SPEED       115200
#define PULSE_OUTPUT_PORT        PORTB
#define PULSE_OUTPUT_DDR         DDRB
#define PULSE_DATA_OFF           0x00
#define PULSE_DATA_ON            0xFF

// External input pins
#define EXT_INPUT_TRIGGER_PIN    2    // External trigger like hall sensor.
#define EXT_INPUT_CLOCK_PIN      5    // Timer clock input for external clock source.
#define EXT_INPUT_PUSH0_PIN      3    // Human push botton trigger input pin.
#define EXT_INPUT_PUSH1_PIN      4    // cylcle over adc modes.
#define EXT_INPUT_ADC0_PIN       18   // train_wait.
#define EXT_INPUT_ADC1_PIN       19   // step_wait.

// Command parseing
#define CMD_MAX_ARGS             8
#define CMD_WHITE_SPACE          " \r\t\n"

// LCD pins
#define LCD_INIT_MSG_TIME        500  // Welcome message timeout
#define LCD_TEST_DOT_TIME        30   // 30ms per col test dot delay
#define LCD_SIZE_ROW             2    // 2 rows
#define LCD_SIZE_COL             20   // 20 columns
#define LCD_RS_PIN               6    // Arduino style pin numbers
#define LCD_E_PIN                7
#define LCD_D0_PIN               14
#define LCD_D1_PIN               15
#define LCD_D2_PIN               16
#define LCD_D3_PIN               17

// Startup defauts
#define DEFAULT_SYS_LCD_TIME     200        // Refresh lcd 5 times per second
#define DEFAULT_SYS_MENU_TIME    4000       // Exit menu after 4 seconds idle
#define DEFAULT_SYS_ADC_TIME     170        // Sample adc 10 times per second
#define DEFAULT_SYS_INPUT_TIME   5          // Read input 200 times a second.
#define DEFAULT_SYS_INPUT_DELAY  300        // Delay after input.
#define DEFAULT_SYS_START_DUTY   50         // Default starting duty for softstart
#define DEFAULT_PULSE_DATA_INIT  0x01       // Start with output 0 set to high
#define DEFAULT_PULSE_STEPS      5          // 5 outputs
#define DEFAULT_PULSE_SEQ_LENGTH 15         // 16bit seq bit data so 15 = max
#define DEFAULT_PULSE_SEQ_DATA   255        // 50% duty cycle
#define DEFAULT_TRAIN_LOOP       1000       // 10000
#define DEFAULT_TRAIN_CLOCK      1          // no prescale
#define DEFAULT_TRAIN_TCNT       65536-160  // 16Mhz/160 = 0.1Mhz clock pulse for timer
#define DEFAULT_STEP_LOOP        0          // Disable step duty
#define DEFAULT_STEP_CLOCK       1          // no prescale
#define DEFAULT_STEP_TCNT        256-160    // duty scale

// All enums, count as int 0,1,2,etc,etc
enum {PULSE_TRIGGER_LOOP,PULSE_TRIGGER_PUSH,PULSE_TRIGGER_EXT};
enum {TRAIN_STATE_IDLE,TRAIN_STATE_RUN,TRAIN_STATE_WAIT_LAST,TRAIN_STATE_WAIT_DUTY};
enum {STEP_STATE_IDLE,STEP_STATE_RUN,STEP_STATE_DONE};
enum {PULSE_MODE_OFF,PULSE_MODE_FLASH,PULSE_MODE_TRAIN,PULSE_MODE_TRAIN_REVERSE,PULSE_MODE_SEQUENCE};
enum {LCD_MENU_OFF, LCD_MENU_SELECT, LCD_MENU_VALUE};

const char pmGetSpaced[]         PROGMEM = "==";
const char pmSetSpaced[]         PROGMEM = "=";
const char pmPulseFire[]         PROGMEM = "PulseFire ";
const char pmPromt[]             PROGMEM = "root@pulsefire: ";
const char pmDone[]              PROGMEM = "done";
const char pmBootLine0[]         PROGMEM = "\r\n# ";
const char pmBootLine1[]         PROGMEM = "\r\n# Created for Magnagas/HHO researchers.\r\n";
const char pmLcdSelect[]         PROGMEM = "Select option ";
const char pmLcdSelectedOption[] PROGMEM = "O: ";
const char pmLcdMultiply[]       PROGMEM = " M:";
const char pmLcdValue[]          PROGMEM = "VALUE: ";
const char pmCmdHelpStart[]      PROGMEM = "# The commands are;\r\n";
const char pmCmdHelpEnd[]        PROGMEM = "# For argument help see source.";
const char pmCmdHelp[]           PROGMEM = "help";
const char pmCmdVersion[]        PROGMEM = "version";
const char pmCmdDefaults[]       PROGMEM = "defaults";
const char pmCmdSave[]           PROGMEM = "save";
const char pmCmdInfo[]           PROGMEM = "info";
const char pmCmdSysLcdTime[]     PROGMEM = "sys_lcd_time";
const char pmCmdSysMenuTime[]    PROGMEM = "sys_menu_time";
const char pmCmdSysAdcTime[]     PROGMEM = "sys_adc_time";
const char pmCmdSysAdcMap[]      PROGMEM = "sys_adc_map";
const char pmCmdSysInputTime[]   PROGMEM = "sys_input_time";
const char pmCmdSysInputDelay[]  PROGMEM = "sys_input_delay";
const char pmCmdSysStartSecs[]   PROGMEM = "sys_start_secs";
const char pmCmdSysStartDuty[]   PROGMEM = "sys_start_duty";
const char pmCmdPulseSteps[]     PROGMEM = "pulse_steps";
const char pmCmdPulseDuty[]      PROGMEM = "pulse_duty";
const char pmCmdPulseTrigger[]   PROGMEM = "pulse_trigger";
const char pmCmdPulseMode[]      PROGMEM = "pulse_mode";
const char pmCmdPulseInit[]      PROGMEM = "pulse_init";
const char pmCmdPulseSeqOff[]    PROGMEM = "pulse_seq_off";
const char pmCmdPulseSeqType[]   PROGMEM = "pulse_seq_type";
const char pmCmdPulseSeqData[]   PROGMEM = "pulse_seq_data";
const char pmCmdPulseSeqLength[] PROGMEM = "pulse_seq_len";
const char pmCmdTrainLoop[]      PROGMEM = "train_loop";
const char pmCmdTrainInc[]       PROGMEM = "train_inc";
const char pmCmdTrainTcnt[]      PROGMEM = "train_tcnt";
const char pmCmdTrainClock[]     PROGMEM = "train_clock";
const char pmCmdStepLoop[]       PROGMEM = "step_loop";
const char pmCmdStepInc[]        PROGMEM = "step_inc";
const char pmCmdStepTcnt[]       PROGMEM = "step_tcnt";
const char pmCmdStepClock[]      PROGMEM = "step_clock";
const char pmInfoCPUFreq[]       PROGMEM = "info_cpu";
const char pmInfoUptime[]        PROGMEM = "info_up";
const char pmPulseData[]         PROGMEM = "pulse_data";
const char pmStepLoopCnt[]       PROGMEM = "step_loop_cnt";
const char pmStepState[]         PROGMEM = "step_state";
const char pmTrainLoopCnt[]      PROGMEM = "train_loop_cnt";
const char pmTrainState[]        PROGMEM = "train_state";
const char pmCalcTrainSpeed[]    PROGMEM = "calc_train_speed";
const char pmCalcTrainLoop[]     PROGMEM = "calc_train_loop";
const char pmCalcTrainFreq[]     PROGMEM = "calc_train_freq";
const char pmCalcStepSpeed[]     PROGMEM = "calc_step_speed";
const char pmCalcStepLoop[]      PROGMEM = "calc_step_loop";
const char pmCalcStepFreqOn[]    PROGMEM = "calc_step_freq_on";
const char pmCalcStepFreqOff[]   PROGMEM = "calc_step_freq_off";

// Array of pointers to all commands.
PGM_P pmCmdList[PMCMDLIST_SIZE] PROGMEM = {
    pmCmdHelp,pmCmdInfo,pmCmdSave,pmCmdVersion,pmCmdDefaults,
    pmCmdSysLcdTime,pmCmdSysMenuTime,pmCmdSysAdcTime,pmCmdSysAdcMap,
    pmCmdSysInputTime,pmCmdSysInputDelay,
    pmCmdSysStartSecs,pmCmdSysStartDuty,
    pmCmdPulseSteps,pmCmdPulseDuty,pmCmdPulseTrigger,pmCmdPulseMode,pmCmdPulseInit,
    pmCmdPulseSeqOff,pmCmdPulseSeqType,pmCmdPulseSeqData,pmCmdPulseSeqLength,
    pmCmdTrainLoop,pmCmdTrainInc,pmCmdTrainTcnt,pmCmdTrainClock,
    pmCmdStepLoop,pmCmdStepInc,pmCmdStepTcnt,pmCmdStepClock
};

// Array of pointers to lcd menu.
PGM_P pmLcdList[PMLCDLIST_SIZE] PROGMEM = {
    pmCmdTrainLoop,pmCmdTrainInc,pmCmdTrainClock,
    pmCmdStepLoop,pmCmdStepInc,pmCmdStepClock,
    pmCmdPulseSteps,pmCmdPulseDuty,pmCmdPulseTrigger,pmCmdPulseMode,pmCmdPulseInit,
    pmCmdPulseSeqOff,pmCmdPulseSeqType,pmCmdPulseSeqData,
    pmCmdSave
};

// PulseFire internal config
typedef struct {
  volatile uint8_t  sys_version;
  volatile uint8_t  sys_struct_size;
  volatile uint16_t sys_lcd_time;
  volatile uint16_t sys_menu_time;
  volatile uint16_t sys_adc_time;
  volatile uint16_t sys_adc_map[6][3];
  volatile uint16_t sys_input_time;
  volatile uint16_t sys_input_delay;
  volatile uint16_t sys_start_secs;
  volatile uint16_t sys_start_duty;
 
  volatile uint16_t pulse_mode;
  volatile uint16_t pulse_data_init;
  volatile uint16_t pulse_steps;
  volatile uint16_t pulse_duty;
  volatile uint16_t pulse_trigger;
  volatile uint16_t pulse_seq_off;
  volatile uint16_t pulse_seq_type;
  volatile uint16_t pulse_seq_data;
  volatile uint16_t pulse_seq_length;
 
  volatile uint16_t train_loop_max_init;
  volatile uint16_t train_loop_max_inc;
  volatile uint16_t train_tcnt;
  volatile uint16_t train_clock;
 
  volatile uint16_t step_loop_max_init;
  volatile uint16_t step_loop_max_inc;
  volatile uint16_t step_tcnt;
  volatile uint16_t step_clock;
} pf_conf_struct;

// PulseFire internal data
typedef struct {
  unsigned long sys_lcd_time_cnt;
  unsigned long sys_menu_time_cnt;
  unsigned long sys_adc_time_cnt;
  unsigned long sys_input_time_cnt;
  volatile uint16_t sys_start_secs_cnt;
  volatile uint16_t sys_start_duty_cnt;

  volatile uint16_t pulse_data;
  volatile uint16_t pulse_duty_cnt;
  volatile uint16_t pulse_seq_cnt;
 
  volatile uint16_t train_state;
  volatile uint16_t train_loop_cnt;
  volatile uint16_t train_loop_max;
 
  volatile uint16_t step_state;
  volatile uint16_t step_loop_cnt;
  volatile uint16_t step_loop_max;
 
} pf_data_struct;

// Create stuctures
pf_data_struct       pf_data;
pf_conf_struct       pf_conf;
pf_conf_struct EEMEM pf_conf_eeprom;

// normal processing variables
char unpstr_buff[UNPSTR_BUFF_SIZE];
int  cmd_buff_idx  = ZERO;
char cmd_buff[CMD_BUFF_SIZE];
int  lcd_menu      = LCD_MENU_OFF;
int  lcd_menu_idx  = ZERO;
int  lcd_menu_mul  = ONE;

// Seqence output index for max 8 outs.
volatile uint8_t ppm_out_data[8] = {
  0,0,0,0,
  0,0,0,0
};

// Seqence data
volatile uint16_t ppm_seq_data[25] = {

  0b1000000000000000, // 93.75%  - 1
  0b1100000000000000,
  0b1110000000000000,
  0b1111000000000000,
  0b1111100000000000,
  0b1111110000000000,
  0b1111111000000000,
  0b1111111100000000, // 50% - 7
  0b1111111110000000,
  0b1111111111000000,
  0b1111111111100000,
  0b1111111111110000,
  0b1111111111111000,
  0b1111111111111100,
  0b1111111111111110, // 6.25%  - 14
 
  0b1010101010101010, // turbe mode 50 %
  0b1010101011111111,
  0b1010101111111111,

  0b1001001111111111, //
  0b1001001001111111,
  0b1001001001001111,
  0b1001001001001001,

  0b1000111111111111, //
  0b1000011100110111,
  0b1000001111100011
};

// init lcd object
LiquidCrystal lcd (
  LCD_RS_PIN,LCD_E_PIN,
  LCD_D0_PIN,LCD_D1_PIN,LCD_D2_PIN,LCD_D3_PIN
);


// Uncopy from program/flash memory to sram
char* UNPSTR(const char* dstring) {
  for (int i=0;i < UNPSTR_BUFF_SIZE;i++) {
    unpstr_buff[i]='\0'; // clean buffer
  }
  int index = 0;
  while (pgm_read_byte(dstring) != 0x00) {
    uint8_t c = pgm_read_byte(dstring++);
    unpstr_buff[index]=c;
    index++;
  }
  return unpstr_buff;
}

// Fill pstr_buff from pointer
char* UNPSTRA(const prog_char** argu) {
  for (int c=0;c < UNPSTR_BUFF_SIZE;c++) {
    unpstr_buff[c]='\0';
  }
  PGM_P p;
  memcpy_P(&p,argu, sizeof(PGM_P));
  strcpy_P(unpstr_buff, p);
  return unpstr_buff;
}

// Parser ascii to unsigned word.
uint16_t atou16(char* s) {
  uint16_t nn = 0;
  char* ss = s;
  while (*ss++) { nn++; }
  uint16_t num=0;
  for (int i=0;i <= nn;i++) {
    if (s[i] >= '0' && s[i] <= '9') {
      num = num * 10 + s[i] -'0';
    } else {
      break;
    }
  }
  return num;
}

// Init all config and data to init state.
void reset_config(void) {
  pf_conf.sys_version          = PULSE_FIRE_VERSION;
  pf_conf.sys_struct_size      = sizeof(pf_conf_struct);
  pf_conf.sys_lcd_time         = DEFAULT_SYS_LCD_TIME;
  pf_conf.sys_menu_time        = DEFAULT_SYS_MENU_TIME;
  pf_conf.sys_adc_time         = DEFAULT_SYS_ADC_TIME;
  for (int i=0;i < 6;i++) {
    pf_conf.sys_adc_map[i][0]  = ZERO;
    pf_conf.sys_adc_map[i][1]  = ZERO;
    pf_conf.sys_adc_map[i][2]  = ZERO;
  }
  pf_conf.sys_input_time       = DEFAULT_SYS_INPUT_TIME;
  pf_conf.sys_input_delay      = DEFAULT_SYS_INPUT_DELAY;
  pf_conf.sys_start_secs       = ZERO;
  pf_conf.sys_start_duty       = DEFAULT_SYS_START_DUTY;
  pf_conf.pulse_mode           = PULSE_MODE_TRAIN;
  pf_conf.pulse_data_init      = DEFAULT_PULSE_DATA_INIT;
  pf_conf.pulse_steps          = DEFAULT_PULSE_STEPS;
  pf_conf.pulse_duty           = ZERO;
  pf_conf.pulse_trigger        = PULSE_TRIGGER_LOOP;
  pf_conf.pulse_seq_off        = ZERO;
  pf_conf.pulse_seq_type       = ZERO;
  pf_conf.pulse_seq_data       = DEFAULT_PULSE_SEQ_DATA;
  pf_conf.pulse_seq_length     = DEFAULT_PULSE_SEQ_LENGTH;
  pf_conf.train_clock          = DEFAULT_TRAIN_CLOCK;
  pf_conf.train_loop_max_init  = DEFAULT_TRAIN_LOOP;
  pf_conf.train_loop_max_inc   = ZERO;
  pf_conf.train_tcnt           = DEFAULT_TRAIN_TCNT;
  pf_conf.step_clock           = DEFAULT_STEP_CLOCK;
  pf_conf.step_loop_max_init   = DEFAULT_STEP_LOOP;
  pf_conf.step_loop_max_inc    = ZERO;
  pf_conf.step_tcnt            = DEFAULT_STEP_TCNT;
}
void reset_data(void) {
  PULSE_OUTPUT_PORT            = PULSE_DATA_OFF;
  pf_data.sys_lcd_time_cnt     = ZERO;
  pf_data.sys_adc_time_cnt     = ZERO;
  pf_data.sys_input_time_cnt   = ZERO;
  pf_data.sys_start_secs_cnt   = ONE;  // is zero after startup.
  pf_data.sys_start_duty_cnt   = ZERO;
  pf_data.pulse_data           = pf_conf.pulse_data_init;
  pf_data.pulse_duty_cnt       = ZERO;
  pf_data.pulse_seq_cnt        = ZERO;
  pf_data.train_loop_cnt       = ZERO;
  pf_data.train_loop_max       = pf_conf.train_loop_max_init;
  if (pf_conf.pulse_trigger   == PULSE_TRIGGER_LOOP) {
    pf_data.train_state        = TRAIN_STATE_RUN;
  } else {
    pf_data.train_state        = TRAIN_STATE_IDLE;
  }
  pf_data.step_loop_cnt        = ZERO;
  pf_data.step_loop_max        = pf_conf.step_loop_max_init;
  pf_data.step_state           = STEP_STATE_IDLE;
 
  // also update default init data for easy reversal.
  if (pf_conf.pulse_mode      == PULSE_MODE_TRAIN_REVERSE && pf_conf.pulse_data_init == DEFAULT_PULSE_DATA_INIT ) {
    pf_conf.pulse_data_init    = (ONE << (pf_conf.pulse_steps - ONE));
  } else {
    pf_conf.pulse_data_init    = DEFAULT_PULSE_DATA_INIT;
  }
}


int convert_clock(int clockScaleMode) {
  int clockScale = 1;
  switch (clockScaleMode) {
    case 1:  clockScale = 1;    break;
    case 2:  clockScale = 8;    break;
    case 3:  clockScale = 64;   break;
    case 4:  clockScale = 256;  break;
    case 5:  clockScale = 1024; break;
    default: clockScale = 1;    break;
  }
  return clockScale;
}
int calc_outputs() {
  if (pf_conf.pulse_mode == PULSE_MODE_FLASH) {
    return 1;
  }
  return pf_conf.pulse_steps;
}
long calc_train_speed() {
  int clockScaleMode = TCCR1B; // todo mask 3 bit
  int clockScale = convert_clock(clockScaleMode);
  long freqTrain = F_CPU / clockScale / (65536-pf_conf.train_tcnt);
  return freqTrain;
}
long calc_step_speed() {
  int clockScaleMode = TCCR2B; // todo mask 3 bit
  int clockScale = convert_clock(clockScaleMode);
  long freqStep = F_CPU / clockScale / (256-pf_conf.step_tcnt);
  return freqStep;
}
long calc_train_loop() {
  if (pf_conf.train_loop_max_init==0) {
    return 0;
  }
  return calc_train_speed() / pf_conf.train_loop_max_init / 10 / calc_outputs() ;
}
long calc_step_loop() {
  if (pf_conf.step_loop_max_init==0) {
    return 0;
  }
  return calc_step_speed() / pf_conf.step_loop_max_init  / 10 / calc_outputs() ;
}
long calc_train_freq() {
  int trainStepLoop = calc_train_loop();
  int dutyStep = trainStepLoop * pf_data.pulse_duty_cnt;
  return ((trainStepLoop / calc_outputs()) + dutyStep) / 2;
}
long calc_step_freq_on() {
  return calc_train_loop() / 2;
}
long calc_step_freq_off() {
  return calc_step_loop() / 2;
}

void cmd_request_freq(void) {

}

void cmd_print_info_value(const char* dstring,uint16_t value) {
  Serial.print(UNPSTR(dstring));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(value);
  Serial.println();
}
void cmd_print_info_value_long(const char* dstring,long value) {
  Serial.print(UNPSTR(dstring));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(value);
  Serial.println();
}
void cmd_print_adc_map_value(int i) {
  Serial.print(i);
  Serial.print(' ');
  Serial.print(pf_conf.sys_adc_map[i][0]);
  Serial.print(' ');
  Serial.print(pf_conf.sys_adc_map[i][1]);
  Serial.print(' ');
  Serial.print(pf_conf.sys_adc_map[i][2]);
}
void cmd_print_info(void) {
  cmd_print_info_value(pmCmdSysLcdTime,        pf_conf.sys_lcd_time);
  cmd_print_info_value(pmCmdSysMenuTime,       pf_conf.sys_menu_time);
  cmd_print_info_value(pmCmdSysAdcTime,        pf_conf.sys_adc_time);
  for (int i=0;i < 6;i++) {
    Serial.print(UNPSTR(pmCmdSysAdcMap));
    Serial.print(i);
    Serial.print(UNPSTR(pmGetSpaced));
    cmd_print_adc_map_value(i);
    Serial.println();
  }
  cmd_print_info_value(pmCmdSysInputTime,      pf_conf.sys_input_time);
  cmd_print_info_value(pmCmdSysInputDelay,     pf_conf.sys_input_delay);
  cmd_print_info_value(pmCmdSysStartSecs,      pf_conf.sys_start_secs);
  cmd_print_info_value(pmCmdSysStartDuty,      pf_conf.sys_start_duty);
  cmd_print_info_value(pmCmdPulseSteps,        pf_conf.pulse_steps);
  cmd_print_info_value(pmCmdPulseDuty,         pf_conf.pulse_duty);
  cmd_print_info_value(pmCmdPulseTrigger,      pf_conf.pulse_trigger);
  cmd_print_info_value(pmCmdPulseMode,         pf_conf.pulse_mode);    
  cmd_print_info_value(pmCmdPulseInit,         pf_conf.pulse_data_init);
  cmd_print_info_value(pmCmdPulseSeqOff,       pf_conf.pulse_seq_off);
  cmd_print_info_value(pmCmdPulseSeqType,      pf_conf.pulse_seq_type);
  cmd_print_info_value(pmCmdPulseSeqData,      pf_conf.pulse_seq_data);
  cmd_print_info_value(pmCmdPulseSeqLength,    pf_conf.pulse_seq_length);
  cmd_print_info_value(pmCmdTrainLoop,         pf_conf.train_loop_max_init);
  cmd_print_info_value(pmCmdTrainInc,          pf_conf.train_loop_max_inc);
  cmd_print_info_value(pmCmdTrainTcnt,         pf_conf.train_tcnt);
  cmd_print_info_value(pmCmdTrainClock,        pf_conf.train_clock);
  cmd_print_info_value(pmCmdStepLoop,          pf_conf.step_loop_max_init);
  cmd_print_info_value(pmCmdStepInc,           pf_conf.step_loop_max_inc);
  cmd_print_info_value(pmCmdStepTcnt,          pf_conf.step_tcnt);
  cmd_print_info_value(pmCmdStepClock,         pf_conf.step_clock);
  cmd_print_info_value(pmPulseData,            pf_data.pulse_data);  
  cmd_print_info_value(pmTrainLoopCnt,         pf_data.train_loop_cnt);
  cmd_print_info_value(pmTrainState,           pf_data.train_state);
  cmd_print_info_value(pmStepLoopCnt,          pf_data.step_loop_cnt);
  cmd_print_info_value(pmStepState,            pf_data.step_state);
  cmd_print_info_value_long(pmCalcTrainSpeed,  calc_train_speed());
  cmd_print_info_value_long(pmCalcTrainLoop,   calc_train_loop());
  cmd_print_info_value_long(pmCalcTrainFreq,   calc_train_freq());
  cmd_print_info_value_long(pmCalcStepSpeed,   calc_step_speed());
  cmd_print_info_value_long(pmCalcStepLoop,    calc_step_loop());
  cmd_print_info_value_long(pmCalcStepFreqOn,  calc_step_freq_on());
  cmd_print_info_value_long(pmCalcStepFreqOff, calc_step_freq_off());
  cmd_print_info_value_long(pmInfoUptime,      millis());
  cmd_print_info_value_long(pmInfoCPUFreq,     F_CPU);
  Serial.print(UNPSTR(pmCmdInfo));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(UNPSTR(pmDone));
}

// Check and parse command
uint16_t cmd_property(char* cmd, char** args,const char* checkCmd,uint16_t value) {
  if (strcmp_P(cmd,checkCmd) != ZERO) {
    return value;
  }
  Serial.print(UNPSTR(checkCmd));
  if (args[0] == NULL) {
    Serial.print(UNPSTR(pmGetSpaced));
  } else {
    Serial.print(UNPSTR(pmSetSpaced));
    value = atou16(args[0]);
  }
  Serial.print(value);
  return value;
}

// execute cmd with the supplied argument
void cmd_execute(char* cmd, char** args) {
  if ( strcmp_P(cmd,pmCmdHelp) == ZERO ) {
    Serial.print(UNPSTR(pmCmdHelpStart));
    for (int i=0;i < PMCMDLIST_SIZE;i++) {
      Serial.print(UNPSTRA(&pmCmdList[i]));
      Serial.println();
    }
    Serial.print(UNPSTR(pmCmdHelpEnd));
    return;
  } else if (strcmp_P(cmd,pmCmdVersion) == ZERO) {
    Serial.print(UNPSTR(pmCmdVersion));
    Serial.print(UNPSTR(pmGetSpaced));
    Serial.print(PULSE_FIRE_VERSION/10 % 10);
    Serial.print('.');
    Serial.print(PULSE_FIRE_VERSION % 10);
    return;
  } else if (strcmp_P(cmd,pmCmdInfo) == ZERO) {
    cmd_print_info();
    return;
  } else if (strcmp_P(cmd,pmCmdDefaults) == ZERO) {
    Serial.print(UNPSTR(pmCmdDefaults));
    reset_config();
    reset_data();
    Serial.print(UNPSTR(pmSetSpaced));
    Serial.print(ONE);
    return;
  } else if (strcmp_P(cmd,pmCmdSave) == ZERO) {
    Serial.print(UNPSTR(pmCmdSave));
    eeprom_write_block((const void*)&pf_conf,(void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
    Serial.print(UNPSTR(pmSetSpaced));
    Serial.print(ONE);
    return;
  } else if (strcmp_P(cmd,pmCmdSysAdcMap) == ZERO) {
    Serial.print(UNPSTR(pmCmdSysAdcMap));
    if (args[0] == NULL) {
      Serial.print(UNPSTR(pmGetSpaced));
      Serial.print(ZERO);
      return;
    }
    uint16_t adc_idx = atou16(args[0]);
    if (args[1] == NULL) {
      Serial.print(UNPSTR(pmGetSpaced));
      cmd_print_adc_map_value(adc_idx);
      return;
    }
    uint16_t adc_map_to  = atou16(args[1]);
    uint16_t adc_map_min = ZERO;
    uint16_t adc_map_max = 65535;
    if (args[2] != NULL) { adc_map_min = atou16(args[2]); }    
    if (args[3] != NULL) { adc_map_max = atou16(args[3]); }    
    pf_conf.sys_adc_map[adc_idx][0] = adc_map_to;
    pf_conf.sys_adc_map[adc_idx][1] = adc_map_min;
    pf_conf.sys_adc_map[adc_idx][2] = adc_map_max;
    Serial.print(UNPSTR(pmSetSpaced));
    cmd_print_adc_map_value(adc_idx);
    return;
  }
 
  // process all get/set properties
  pf_conf.sys_lcd_time        = cmd_property(cmd,args,pmCmdSysLcdTime,    pf_conf.sys_lcd_time);
  pf_conf.sys_menu_time       = cmd_property(cmd,args,pmCmdSysMenuTime,   pf_conf.sys_menu_time);
  pf_conf.sys_adc_time        = cmd_property(cmd,args,pmCmdSysAdcTime,    pf_conf.sys_adc_time);
  pf_conf.sys_input_time      = cmd_property(cmd,args,pmCmdSysInputTime,  pf_conf.sys_input_time);
  pf_conf.sys_input_delay     = cmd_property(cmd,args,pmCmdSysInputDelay, pf_conf.sys_input_delay);
  pf_conf.sys_start_secs      = cmd_property(cmd,args,pmCmdSysStartSecs,  pf_conf.sys_start_secs);
  pf_conf.sys_start_duty      = cmd_property(cmd,args,pmCmdSysStartDuty,  pf_conf.sys_start_duty);
  pf_conf.pulse_steps         = cmd_property(cmd,args,pmCmdPulseSteps,    pf_conf.pulse_steps);
  pf_conf.pulse_duty          = cmd_property(cmd,args,pmCmdPulseDuty,     pf_conf.pulse_duty);
  pf_conf.pulse_trigger       = cmd_property(cmd,args,pmCmdPulseTrigger,  pf_conf.pulse_trigger);
  pf_conf.pulse_mode          = cmd_property(cmd,args,pmCmdPulseMode,     pf_conf.pulse_mode);
  pf_conf.pulse_data_init     = cmd_property(cmd,args,pmCmdPulseInit,     pf_conf.pulse_data_init);
  pf_conf.pulse_seq_off       = cmd_property(cmd,args,pmCmdPulseSeqOff,   pf_conf.pulse_seq_off);
  pf_conf.pulse_seq_type      = cmd_property(cmd,args,pmCmdPulseSeqType,  pf_conf.pulse_seq_type);
  pf_conf.pulse_seq_data      = cmd_property(cmd,args,pmCmdPulseSeqData,  pf_conf.pulse_seq_data);
  pf_conf.pulse_seq_length    = cmd_property(cmd,args,pmCmdPulseSeqLength,pf_conf.pulse_seq_length);
  pf_conf.train_loop_max_init = cmd_property(cmd,args,pmCmdTrainLoop,     pf_conf.train_loop_max_init);
  pf_conf.train_loop_max_inc  = cmd_property(cmd,args,pmCmdTrainInc,      pf_conf.train_loop_max_inc);
  pf_conf.train_tcnt          = cmd_property(cmd,args,pmCmdTrainTcnt,     pf_conf.train_tcnt);
  pf_conf.train_clock         = cmd_property(cmd,args,pmCmdTrainClock,    pf_conf.train_clock);
  pf_conf.step_loop_max_init  = cmd_property(cmd,args,pmCmdStepLoop,      pf_conf.step_loop_max_init);
  pf_conf.step_loop_max_inc   = cmd_property(cmd,args,pmCmdStepInc,       pf_conf.step_loop_max_inc);
  pf_conf.step_tcnt           = cmd_property(cmd,args,pmCmdStepTcnt,      pf_conf.step_tcnt);
  pf_conf.step_clock          = cmd_property(cmd,args,pmCmdStepClock,     pf_conf.step_clock);
   
  // Correct overflow, 0 = overflow value, so gives interrupt loop.
  if (pf_conf.train_tcnt == ZERO) {
    pf_conf.train_tcnt=ONE;
  }
  if (pf_conf.step_tcnt == ZERO) {
    pf_conf.step_tcnt=ONE;
  }

  // Save updated clock value to registers
  if (strcmp_P(cmd,pmCmdTrainClock) == ZERO && args[0] != NULL) {
    TCCR1B = pf_conf.train_clock; // no masking...
  }
  if (strcmp_P(cmd,pmCmdStepClock) == ZERO && args[0] != NULL) {
    TCCR2B = pf_conf.step_clock; // no masking...    
  }

  // reset data when trigger or mode is changed
  if (strcmp_P(cmd,pmCmdPulseTrigger) == ZERO && args[0] != NULL) {
    reset_data();
  }
  if (strcmp_P(cmd,pmCmdPulseMode) == ZERO && args[0] != NULL) {
    reset_data();
  }
}

// Parse the cmd from the cmd_buff
void cmd_parse(void) {
  int idx = 0;
  char *cmd, *ptr, *args[CMD_MAX_ARGS];
  if (strtok((char *)cmd_buff,CMD_WHITE_SPACE) == NULL) {
    Serial.print(UNPSTR(pmPromt));
    return; // no command given so just print new promt.
  }
  cmd = (char *)cmd_buff;
  while( (ptr = strtok(NULL,CMD_WHITE_SPACE)) != NULL) {
    args[idx] = ptr;
    if (++idx == (CMD_MAX_ARGS-1)) {
      break;
    }
  }
  args[idx] = NULL;
  cmd_execute(cmd,args);
  Serial.println();
  Serial.print(UNPSTR(pmPromt));
}

// Check for data from console and put in cmd_buff
void loop_serial(void) {
  int readBytes = Serial.available();
  if (readBytes==ZERO) {
    return;
  }
  boolean cmd_process = false;
  for (int b=ZERO;b < readBytes;b++) {
    int c = Serial.read();
    Serial.write(c);
    if (cmd_buff_idx>CMD_BUFF_SIZE) {
      cmd_buff_idx = ZERO; // protect against to long input
    }
    if (c=='\b') {
      cmd_buff[cmd_buff_idx] = '\0';// backspace
      cmd_buff_idx--;
      Serial.write(' ');
      Serial.write(c);
    } else if (c=='\n') {
      cmd_buff[cmd_buff_idx] = '\0';// newline
      cmd_process=true;
      cmd_buff_idx=ZERO;
    } else {
      cmd_buff[cmd_buff_idx] = c;   // store in buffer
      cmd_buff_idx++;
    }
  }
  if (cmd_process == true) {
     cmd_parse();
  }
}

// process/print or print value only of selected menu items
void loop_input_menu_item(boolean count_down,boolean print_only,boolean print_header) {
  if (print_header) {
    lcd.clear();
    lcd.print(UNPSTR(pmLcdSelectedOption));
    lcd.print(UNPSTRA(&pmLcdList[lcd_menu_idx]));
    lcd.print(UNPSTR(pmLcdMultiply));
    lcd.print(lcd_menu_mul);
    lcd.setCursor(0,1);
    lcd.print(UNPSTR(pmLcdValue));
  }
  int m = lcd_menu_mul;
  int v = lcd_menu_idx;
  if (count_down) {
    v = v+100; // default is up, so this is down.
  }
  if (print_only) {
    if (count_down) {
      v = v+100;
    } else {
      v = v+200;
    }
  }
  switch(v) {
    case 0:   pf_conf.train_loop_max_init+=2*m;
    case 100: pf_conf.train_loop_max_init-=m;
    case 200: lcd.print(pf_conf.train_loop_max_init); break;
    case 1:   pf_conf.train_loop_max_inc+=2*m;
    case 101: pf_conf.train_loop_max_inc-=m;
    case 201: lcd.print(pf_conf.train_loop_max_inc); break;
    case 2:   pf_conf.train_clock+=2*m;
    case 102: pf_conf.train_clock-=m;
    case 202: lcd.print(pf_conf.train_clock); break;
    case 3:   pf_conf.step_loop_max_init+=2*m;
    case 103: pf_conf.step_loop_max_init-=m;
    case 203: lcd.print(pf_conf.step_loop_max_init); break;
    case 4:   pf_conf.step_loop_max_inc+=2*m;
    case 104: pf_conf.step_loop_max_inc-=m;
    case 204: lcd.print(pf_conf.step_loop_max_inc); break;
    case 5:   pf_conf.step_clock+=2*m;
    case 105: pf_conf.step_clock-=m;
    case 205: lcd.print(pf_conf.step_clock); break;
    case 6:   pf_conf.pulse_steps+=2*m;
    case 106: pf_conf.pulse_steps-=m;
    case 206: lcd.print(pf_conf.pulse_steps); break;
    case 7:   pf_conf.pulse_duty+=2*m;
    case 107: pf_conf.pulse_duty-=m;
    case 207: lcd.print(pf_conf.pulse_duty); break;
    case 8:   pf_conf.pulse_trigger+=2*m;
    case 108: pf_conf.pulse_trigger-=m;reset_data();
    case 208: lcd.print(pf_conf.pulse_trigger); break;
    case 9:   pf_conf.pulse_mode+=2*m;
    case 109: pf_conf.pulse_mode-=m;reset_data();
    case 209: lcd.print(pf_conf.pulse_mode); break;
    case 10:  pf_conf.pulse_data_init+=2*m;
    case 110: pf_conf.pulse_data_init-=m;
    case 210: lcd.print(pf_conf.pulse_data_init); break;
    case 11:  pf_conf.pulse_seq_off+=2*m;
    case 111: pf_conf.pulse_seq_off-=m;
    case 211: lcd.print(pf_conf.pulse_seq_off); break;
    case 12:  pf_conf.pulse_seq_type+=2*m;
    case 112: pf_conf.pulse_seq_type-=m;
    case 212: lcd.print(pf_conf.pulse_seq_type); break;
    case 13:  pf_conf.pulse_seq_data+=2*m;
    case 113: pf_conf.pulse_seq_data-=m;
    case 213: lcd.print(pf_conf.pulse_seq_data); break;
    case 14:
    case 114:
    case 214:
          eeprom_write_block((const void*)&pf_conf,(void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
          lcd.print(UNPSTR(pmDone));
          break;
    default:
          break;
  }  
}

// Check for user input via buttons.
void loop_input(unsigned long current_time) {
  if (current_time < pf_data.sys_input_time_cnt) {
    return;
  }
  pf_data.sys_input_time_cnt = current_time + pf_conf.sys_input_time;
 
  int input0 = digitalRead(EXT_INPUT_PUSH0_PIN);
  int input1 = digitalRead(EXT_INPUT_PUSH1_PIN);
  if (lcd_menu != LCD_MENU_OFF && input0 == HIGH && input1 == HIGH) {
    if (current_time < pf_data.sys_menu_time_cnt) {
      return; // no input to process
    }
    lcd_menu     = LCD_MENU_OFF;
    lcd_menu_idx = ZERO;
    lcd_menu_mul = ONE;
    return; // exit menu after idle counter timeout
  }
  pf_data.sys_menu_time_cnt = current_time + pf_conf.sys_menu_time;
 
  if (lcd_menu == LCD_MENU_OFF) {
    if (input0 == LOW) {
      lcd_menu     = LCD_MENU_SELECT;
      lcd_menu_idx = ZERO;
    }
    if (input1 == LOW) {
      if (pf_conf.pulse_trigger != PULSE_TRIGGER_PUSH) {
        return; // we only check for button if it is selected as source
      }
      if (pf_data.train_state == TRAIN_STATE_RUN) {
        return; // we just fired it so pulse train is still/has running.
      }
      pf_data.pulse_data  = pf_conf.pulse_data_init;
      pf_data.train_state = TRAIN_STATE_RUN;
    }
  } else if (lcd_menu == LCD_MENU_SELECT) {
    if (input0 == LOW) {      
      if (lcd_menu_idx == PMLCDLIST_SIZE) {
        lcd_menu_idx = ZERO;
      }
      lcd.clear();
      lcd.print(UNPSTR(pmLcdSelect));
      lcd.setCursor(ZERO,ONE);
      lcd.print(UNPSTRA(&pmLcdList[lcd_menu_idx]));
      lcd.setCursor(LCD_SIZE_COL-5,ONE); // have 5 chars for value
      loop_input_menu_item(false,true,false);
      lcd_menu_idx++;
    }
    if (input1 == LOW) {
      lcd_menu_idx--; // go to selected value
      lcd_menu=LCD_MENU_VALUE;
      loop_input_menu_item(true,true,true);
    }
  } else if (lcd_menu == LCD_MENU_VALUE) {
    if (input0 == LOW && input1 == LOW) {
      lcd_menu_mul = lcd_menu_mul * 10;  // make up&down 10faster
    }
    loop_input_menu_item(input1==LOW,false,true);
  }
  _delay_ms(pf_conf.sys_input_delay);
}

// read out analog values.
void loop_input_adc(unsigned long current_time) {
  if (current_time < pf_data.sys_adc_time_cnt) {
    return;
  }
  pf_data.sys_adc_time_cnt = current_time + pf_conf.sys_adc_time;

  boolean output_mode = true;  
  int pin_idx = 14;
  for (int i=0;i < 6;i++) {
    if (output_mode == true) {
      if (i < 4) {
         pin_idx++;
         continue; // only read 4 & 5 for now, until out_mode is there
      }
    }
    int valueAdc = analogRead(pin_idx);
    pin_idx++;
    if (pf_conf.sys_adc_map[i][0] == ZERO) {
      continue; // no mapping
    }
    // map to normal and assign to value
    valueAdc = map(valueAdc,ZERO,ADC_VALUE_MAX,pf_conf.sys_adc_map[i][1],pf_conf.sys_adc_map[i][2]);
    switch (pf_conf.sys_adc_map[i][0]) {
      case  1:pf_conf.train_loop_max_init = valueAdc; break;
      case  2:pf_conf.train_loop_max_inc  = valueAdc; break;
      case  3:pf_conf.train_tcnt          = valueAdc; break;
      case  4:pf_conf.train_clock         = valueAdc; break;
      case  5:pf_conf.step_loop_max_init  = valueAdc; break;
      case  6:pf_conf.step_loop_max_inc   = valueAdc; break;
      case  7:pf_conf.step_tcnt           = valueAdc; break;
      case  8:pf_conf.step_clock          = valueAdc; break;
      case  9:pf_conf.pulse_duty          = valueAdc; break;
      case 10:pf_conf.pulse_mode          = valueAdc; break;
      case 11:pf_conf.pulse_trigger       = valueAdc; break;
      case 12:pf_conf.pulse_seq_off       = valueAdc; break;
      case 13:pf_conf.pulse_seq_type      = valueAdc; break;
      case 14:pf_conf.pulse_seq_data      = valueAdc; break;
      default: break;
    }
  }
}

// refesh the lcd
void loop_lcd(unsigned long current_time) {
  if (current_time < pf_data.sys_lcd_time_cnt) {
    return; // wait until refresh
  }
  pf_data.sys_lcd_time_cnt = current_time + pf_conf.sys_lcd_time;
  if (lcd_menu != LCD_MENU_OFF) {
    return; // we are in menu mode.
  }
  lcd.clear();
  lcd.print("tr: ");
  lcd.print(pf_conf.train_loop_max_init);
  lcd.setCursor(10,0);
  lcd.print("hz: ");
  lcd.print(calc_train_freq());
  lcd.setCursor(ZERO,ONE);
  lcd.print("so: ");
  lcd.print(calc_step_freq_on());
  lcd.setCursor(10,1);
  lcd.print("sf: ");
  lcd.print(calc_step_freq_off());
}

void setup_config(void) {
  uint8_t pfVersion = eeprom_read_byte((uint8_t*)ZERO);
  uint8_t pfStructSize = eeprom_read_byte((uint8_t*)ONE);
  if (pfVersion==PULSE_FIRE_VERSION && pfStructSize==sizeof(pf_conf_struct)) {
    eeprom_read_block((void*)&pf_conf,(const void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
  } else {
    reset_config(); // if newer/other/none version&size in flash then
    reset_data();   // reset all to defaults and save.
    eeprom_write_block((const void*)&pf_conf,(void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
  }
  reset_data();
}

void setup_input(void) {
  // external trigger pin
  EIMSK |= (1 << INT0);   // Enable INT0 External Interrupt
  MCUCR |= (1 << ISC01);  // Falling-Edge Triggered INT0
  pinMode      (EXT_INPUT_TRIGGER_PIN,INPUT);
  digitalWrite (EXT_INPUT_TRIGGER_PIN,HIGH);

  // clk source pin
  pinMode      (EXT_INPUT_CLOCK_PIN,INPUT);
  digitalWrite (EXT_INPUT_CLOCK_PIN,HIGH);

  // UI buttons
  pinMode      (EXT_INPUT_PUSH0_PIN,INPUT);
  digitalWrite (EXT_INPUT_PUSH0_PIN,HIGH);
  pinMode      (EXT_INPUT_PUSH1_PIN,INPUT);
  digitalWrite (EXT_INPUT_PUSH1_PIN,HIGH);
}

void setup_output(void) {
  PULSE_OUTPUT_DDR  = 0xFF;
  PULSE_OUTPUT_PORT = PULSE_DATA_OFF;
}

void setup_serial(void) {
  Serial.begin(PULSE_SERIAL_SPEED);
  Serial.print(UNPSTR(pmBootLine0));
  Serial.print(UNPSTR(pmPulseFire));
  Serial.print(UNPSTR(pmCmdVersion));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(PULSE_FIRE_VERSION/10 % 10);
  Serial.print('.');
  Serial.print(PULSE_FIRE_VERSION % 10);
  Serial.print(UNPSTR(pmBootLine1));
  Serial.print(UNPSTR(pmPromt));
}

void setup_timers(void) {
  // 16bit timer used for pulse steps.
  TCCR1B = DEFAULT_TRAIN_CLOCK;// start timer without prescaler
  TIMSK1|= (ONE << TOIE1);     // enable overflow int
  TCNT1  = ZERO;               // soft start
 
  // 8bit timer used for pulse step duty
  TCCR2B = DEFAULT_STEP_CLOCK;
  TIMSK2|= (ONE << TOIE2);
  TCNT2  = ZERO;
}

void setup_lcd(void) {
  lcd.begin(LCD_SIZE_COL, LCD_SIZE_ROW);
  lcd.print(UNPSTR(pmPulseFire));
  lcd.setCursor(0,1);
  lcd.print(UNPSTR(pmCmdVersion));
  lcd.print(' ');
  lcd.print(PULSE_FIRE_VERSION/10 % 10);
  lcd.print('.');
  lcd.print(PULSE_FIRE_VERSION % 10);
  _delay_ms(LCD_INIT_MSG_TIME);
  for (int i=0;i <= LCD_SIZE_COL;i++) {
    lcd.setCursor(LCD_SIZE_COL-i,0);
    lcd.write('.');
    lcd.setCursor(i,1);
    lcd.write('.');
    _delay_ms(LCD_TEST_DOT_TIME);
  }
  for (int i=0;i <= LCD_SIZE_COL;i++) {
    lcd.setCursor(LCD_SIZE_COL-i,0);
    lcd.write(' ');
    lcd.setCursor(i,1);
    lcd.write(' ');
    _delay_ms(LCD_TEST_DOT_TIME);
  }
  _delay_ms(LCD_TEST_DOT_TIME*5);
}

void int_soft_start(void) {
  long sys_up_secs = millis()/1000;
  if (sys_up_secs == ZERO) {
    sys_up_secs = ONE; // very fast cpu here.
  }
  pf_data.sys_start_secs_cnt = sys_up_secs;
  if (pf_data.sys_start_secs_cnt > pf_conf.sys_start_secs) {
    pf_data.sys_start_secs_cnt = ZERO; // we are done with startup.
  }
  int duty_step = (pf_conf.sys_start_duty - pf_conf.pulse_duty) / pf_conf.sys_start_secs;
  if (duty_step==ZERO) {
    duty_step = ONE; // some value else give no change
  }
  int startup_duty = pf_conf.sys_start_duty - (pf_data.sys_start_secs_cnt * duty_step);
  if (startup_duty < ZERO) {
    startup_duty = ONE; // make sure wait does not loop.
  }
  if (pf_data.pulse_data == pf_conf.pulse_data_init) {
    PULSE_OUTPUT_PORT = PULSE_DATA_OFF;
    pf_data.pulse_duty_cnt++;
    if (pf_data.pulse_duty_cnt < startup_duty) {
      return;
    }
    pf_data.pulse_duty_cnt = ZERO;
    pf_data.train_state    = TRAIN_STATE_IDLE;
  }
}
void int_pulse_step(void) {
  if (pf_data.step_state == STEP_STATE_RUN) {
    return;
  }
  if (pf_data.step_state == STEP_STATE_IDLE) {
    PULSE_OUTPUT_PORT  = PULSE_DATA_OFF; // Switch all off while waiting..
    pf_data.step_state = STEP_STATE_RUN; // start run
    TCNT2              = 255-3;          // run asp
    return;
  }
  pf_data.step_state = STEP_STATE_IDLE; // done with step duty.
}
void int_pulse_mode_flash(void) {
  if (pf_data.pulse_data == PULSE_DATA_ON) {
    pf_data.pulse_data = PULSE_DATA_OFF;
  } else {
    pf_data.pulse_data = PULSE_DATA_ON;
  }
}
void int_pulse_mode_seq(void) {
  uint16_t seq_data = 0;
  if (pf_conf.pulse_seq_type == ZERO) {
    seq_data = pf_conf.pulse_seq_data;
  } else {
    seq_data = ppm_seq_data[pf_conf.pulse_seq_type];
  }
  uint8_t out_data = 0;
  int out = pf_data.pulse_seq_cnt;
  uint8_t out_bit = (seq_data >> ppm_out_data[out]) & ONE;
  out_data |= (out_bit << out);
  if (ppm_out_data[out] == ZERO) {
    ppm_out_data[out] = pf_conf.pulse_seq_length;
    pf_data.pulse_seq_cnt++; // goto next step
  } else {
    ppm_out_data[out]--;
  }
  if (pf_conf.pulse_seq_off > 0 && pf_conf.pulse_seq_off > ppm_out_data[out]) {
    out = pf_data.pulse_seq_cnt + ONE;
    uint8_t out_bit = (seq_data >> ppm_out_data[out]) & ONE;
    out_data |= (out_bit << out);
    if (ppm_out_data[out] == ZERO) {
      ppm_out_data[out] = pf_conf.pulse_seq_length;
    } else {
      ppm_out_data[out]--;
    }
  }
  pf_data.pulse_data = out_data;
  if (pf_data.pulse_seq_cnt > (pf_conf.pulse_steps - ONE)) {
    pf_data.pulse_seq_cnt  = ZERO;
    pf_data.pulse_data     = PULSE_DATA_OFF;
    if (pf_conf.pulse_duty > ZERO) {
      pf_data.train_state  = TRAIN_STATE_WAIT_DUTY;
    }
    if (pf_conf.pulse_trigger > ZERO) {
      pf_data.train_state  = TRAIN_STATE_WAIT_LAST;
    }
  }
}

ISR(INT0_vect) {
  if (pf_conf.pulse_trigger == PULSE_TRIGGER_EXT) {
    pf_data.pulse_data  = pf_conf.pulse_data_init;
    pf_data.train_state = TRAIN_STATE_RUN;
  }
}

ISR(TIMER1_OVF_vect) {
  // set timer counter to start value so we can make timed nicely
  TCNT1 = pf_conf.train_tcnt;
 
  if (pf_conf.pulse_trigger != PULSE_TRIGGER_LOOP && pf_data.train_state == TRAIN_STATE_IDLE) {
    return;// disable when in manuale trigger fire.
  }
   
  // time step with counter
  pf_data.train_loop_cnt++;
  if (pf_data.train_loop_cnt < pf_data.train_loop_max) {
    return;
  }
  pf_data.train_loop_cnt = ZERO;

  // Check for soft startup
  if (pf_data.sys_start_secs_cnt > ZERO && pf_conf.sys_start_secs > ZERO) {
    int_soft_start();
  }

  // Check for offtime check wait
  if (pf_data.step_loop_max > ZERO) {
    int_pulse_step();
  }
 
  // use - for letting last output time correctly until off.
  if (pf_data.train_state == TRAIN_STATE_WAIT_LAST) {
    PULSE_OUTPUT_PORT      = PULSE_DATA_OFF;
    pf_data.train_state    = TRAIN_STATE_IDLE;
    return;
  }
  if (pf_data.train_state == TRAIN_STATE_WAIT_DUTY) {
    PULSE_OUTPUT_PORT      = PULSE_DATA_OFF;
    pf_data.pulse_duty_cnt++;
    if (pf_data.pulse_duty_cnt < pf_conf.pulse_duty) {
      return;
    }
    pf_data.pulse_duty_cnt = ZERO;
    pf_data.train_state    = TRAIN_STATE_IDLE;
  }

  // Set data to outputs
  PULSE_OUTPUT_PORT = pf_data.pulse_data;
 
  // speed up after each step
  if (pf_conf.train_loop_max_inc > ZERO && pf_data.train_loop_max > pf_conf.train_loop_max_inc) {
    pf_data.train_loop_max = pf_data.train_loop_max-pf_conf.train_loop_max_inc;
  }
  if (pf_data.pulse_data == pf_conf.pulse_data_init) {
    pf_data.train_loop_max = pf_conf.train_loop_max_init;
  }
 
  // Change pulse_data to next postition defined by pulse_mode
  switch (pf_conf.pulse_mode) {
    case PULSE_MODE_TRAIN:         pf_data.pulse_data = pf_data.pulse_data << ONE; break;
    case PULSE_MODE_TRAIN_REVERSE: pf_data.pulse_data = pf_data.pulse_data >> ONE; break;
    case PULSE_MODE_FLASH:         int_pulse_mode_flash(); break;
    case PULSE_MODE_SEQUENCE:      int_pulse_mode_seq();   break;
    case PULSE_MODE_OFF:
    default:                       pf_data.pulse_data = PULSE_DATA_OFF; break;
  }
   
  // check for output rotation, todo change to shorter
  if (pf_conf.pulse_mode == PULSE_MODE_TRAIN) {
    if (pf_data.pulse_data > (ONE << (pf_conf.pulse_steps - ONE))) {
      pf_data.pulse_data     = pf_conf.pulse_data_init;
      if (pf_conf.pulse_duty > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_DUTY;
      }
      if (pf_conf.pulse_trigger > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_LAST;
      }
    }
  } else if (pf_conf.pulse_mode == PULSE_MODE_TRAIN_REVERSE) {
    if (pf_data.pulse_data == ZERO) {
      pf_data.pulse_data     = pf_conf.pulse_data_init;
      if (pf_conf.pulse_duty > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_DUTY;
      }
      if (pf_conf.pulse_trigger > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_LAST;
      }
    }
  }
}

ISR(TIMER2_OVF_vect) {  
  TCNT2 = pf_conf.step_tcnt;
  if (pf_data.step_state != STEP_STATE_RUN) {
    return;
  }
  pf_data.step_loop_cnt++;
  if (pf_data.step_loop_cnt < pf_data.step_loop_max) {
    return;
  }
  if (pf_conf.step_loop_max_inc > ZERO && pf_data.step_loop_max > pf_conf.step_loop_max_inc) {
    pf_data.step_loop_max = pf_data.step_loop_max-pf_conf.step_loop_max_inc;
  }
  if (pf_data.pulse_data == pf_conf.pulse_data_init) {
    pf_data.step_loop_max = pf_conf.step_loop_max_init;
  }
  pf_data.step_loop_cnt  = ZERO;              // reset counter for next run.
  pf_data.step_state     = STEP_STATE_DONE;   // reset state to next step
  pf_data.train_loop_cnt = pf_data.train_loop_max; // skip next step delay
  TCNT1                  = 65535-3;           // run asap next step
}

void setup() {
  setup_config();
  setup_input();
  setup_output();
  setup_timers();
  setup_serial();
  setup_lcd();
}
 
void loop() {
  unsigned long time = millis();
  loop_input(time);
  loop_input_adc(time);
  loop_lcd(time);
  loop_serial();
}

note: remove spaces from include statements after copy from forum.
note2: Also added project as attachment because I get the above code not correct copy pasted.

Changed a lot;
- serial speed 115200
- config can be saved in eeprom
- soft startup via extra off duty
- mappable analog input to variable with range
- 'WIP' code for pulse seqence with offset into next step
- simple lcd ui via 2 button:
  lcd_menu_off; push0=enter menu, push1=manual trigger
  lcd_menu_sel; push0=next items,  push1=change value
  lcd_menu_val; push0=+ , push1=- , both is *10

Now about 30 settings (and 15 in lcd)
Code: [Select]
root@pulsefire: info
sys_lcd_time==200
sys_menu_time==4000
sys_adc_time==170
sys_adc_map0==0 0 0 0
sys_adc_map1==1 0 0 0
sys_adc_map2==2 0 0 0
sys_adc_map3==3 0 0 0
sys_adc_map4==4 1 30 300
sys_adc_map5==5 12 0 15
sys_input_time==5
sys_input_delay==300
sys_start_secs==0
sys_start_duty==50
pulse_steps==5
pulse_duty==0
pulse_trigger==0
pulse_mode==4
pulse_init==1
pulse_seq_off==6
pulse_seq_type==0
pulse_seq_data==65535
pulse_seq_len==15
train_loop==213
train_inc==0
train_tcnt==65376
train_clock==1
step_loop==0
step_inc==0
step_tcnt==96
step_clock==1
pulse_data==1
train_loop_cnt==140
train_state==1
step_loop_cnt==0
step_state==0
calc_train_speed==100000
calc_train_loop==9
calc_train_freq==0
calc_step_speed==100000
calc_step_loop==0
calc_step_freq_on==4
calc_step_freq_off==0
info_up==409868
info_cpu==16000000
info==done
root@pulsefire:

The phase shifting is now almost in via the seqence and offset but not fully correct yet, next todo request/calc freq and validate input.
And wanna check 8 bit or more output mode.
Is 8 outputs enough for push / pull setups or run 2 pulse fires ?
yes, 8 is enough, really the 6 you have is enough? where you going to to put the other 2?

got it loaded but the d4 push button dont work?

did you change any of the I/O?

i was awaiting this update ! :) thank you and the attachment of the code was alot better idea!

here is the setup i have,

see attachment


firepinto

RE: EPG Testing sequential pulsar using the Arduino
« Reply #32, on May 5th, 2011, 06:54 PM »
That looks sweet!  Did you make the acrylic  piece or can you buy those?  So far I've destroyed any acrylic I've tried to cut. lol
Quote from Rwg42985 on May 5th, 2011, 06:47 PM
Quote from txqNL on May 5th, 2011, 06:00 PM
Quote from Rwg42985 on May 1st, 2011, 05:37 PM
also if we just have a push button interface with the LCD display that would be cool!
this way we can do away with the pc...??? that can be later... but that would make it easy to use with no pc.
new version;
Code: [Select]

/*

pulse-fire - Automatic Pulse Fire Seqence Generator.
Created 26-Apr-2011 by Willem Cazander
All source is copyfree for all.

Arduino Hardware:
D0/D1  = Rx/Tx Serial console (is mapped to usb on Arduino)
D2     = Interupt hardware trigger for pulse train via hall sensor (pulse_trigger=2)
D3     = Input0 Push button to the ground for menu and add
D4     = Input1 Push button to the ground for trigger/select/subtract
D5     = Interups hardware for timer clock (pulse_clock=6or7)
D6     = LCD RS
D7     = LCD E
D8-D13 = Max 6 outputs to LED(+R) to ground.
A0-A3  = LCD DATA
A4     = Analog input4
A5     = Analog input5
(note: at least one output is needed all the other I/O is optional.)

Serial user interface;
(note: setup 115200b with Newline on enter/return)

root@pulsefire: help
# The commands are;
help               - Shows this
info               - Shows all possible information
save               - Save config value into eeprom
version            - Print the version for parsers
defaults           - Reverts the config values to defaults. (does not save!)
sys_lcd_time       - ms delay before next refresh
sys_menu_time      - ms idle before exit menu
sys_adc_time       - ms delay before next analog readout
sys_adc_map        - Maps analog input to variables with range
sys_input_time     - ms delay before next push button readout
sys_input_delay    - ms delay after push button pressed
sys_start_secs     - Soft start in seconds
sys_start_duty     - Soft start begin duty (should always be bigger then pulse_duty)
pulse_steps        - Number of outputs
pulse_duty         - Off duty after pulse train
pulse_trigger      - Pulse train trigger, 0=loop,1=Input1,2=extern)
pulse_mode         - Pulse train mode, 0=off,1=flash,2=train(def),3=reverse,4=seqence
pulse_init         - Start data for ouput train pulse shifting
pulse_seq_off      - Seqence offset between next step
pulse_seq_type     - Seqence data type,0=pulse_seq_data,1-20 interal pulse seqences
pulse_seq_data     - Custom seqence data 0-65535
pulse_seq_len      - Seqence data length max 15
train_loop         - 16 bit, loop counter of interrupts from clock of timer.
train_inc          - Auto increase speed of train_loop nice max is 1/6 of train_loop
train_tcnt         - Start value of train timer counter (16bit)
train_clock        - Clock prescaler for train timer
step_loop          - Off duty time between train steps, via seperate timer
step_inc           - Auto increase speed of train_loop nice max is 1/6 of step_loop
step_tcnt          - Start value of off duty timer counter (8bit)
step_clock         - Clock prescaler for off duty timer

Allmost all commands have getter and setter interface like this;

root@pulsefire: train_loop
train_wait==2345
root@pulsefire: train_loop 2000
train_wait=2000

root@pulsefire: pulse_trigger 1
pulse_trigger=1
Trigger determents what triggers the next pulse train.
0 - Continues (default)
1 - Manual trigger via button
2 - External trigger on edge

root@pulsefire: pulse_mode 0
pulse_mode=0
The pulse mode it the output type
0 - Off, so no output
1 - Flashing
2 - Pulse train (default)
3 - Pulse train reverse
4 - Seqenceing

root@pulsefire: sys_adc_map 4 1 30 300
Maps analog input4 to train_loop with range from 30 to 300.

root@pulsefire: train_tcnt
train_tcnt=65520
16bit Timer counter start index after overflow default set to 65520 for 1Mhz clock values;
The four nice 10x numbers are;
65520 = 16000000/(65536-(65536-16)) = 1000000 = 1Mhz
65376 = 16000000/(65536-(65536-160)) = 100000 = 100Khz
63936 = 16000000/(65536-(65536-1600)) = 10000 = 10Khz
49536 = 16000000/(65536-(65536-16000)) = 1000 = 1Khz

root@pulsefire: train_clock 2
train_clock=2
Clock values 0-7 default to 1;
CS12 CS11 CS10 Description
0 0 0 No clock source (Timer/Counter stopped).
0 0 1 clkI/O/1 (No prescaling)
0 1 0 clkI/O/8 (From prescaler)
0 1 1 clkI/O/64 (From prescaler)
1 0 0 clkI/O/256 (From prescaler)
1 0 1 clkI/O/1024 (From prescaler)
1 1 0 External clock source on T1 pin. Clock on falling edge.
1 1 1 External clock source on T1 pin. Clock on rising edge.
note: T1 is connected to D5


ASCII ART TABLE NEED MORE..

T1 -
T2 -
T3 -
T4 -


  +---------------T4--------------+    
  +----+                          +----+
D0|    |                          |    |
  |    |                          |    |
--+    +--------------------------+    +---
  +-T1-+-T2-+    
            +----+
Dx          |    |
            |    |
------------+    +-------------------------
                         +---T3---+
                    +----+
Dlast               |    |
                    |    |
--------------------+    +-----------------

TODO LIST:
- fix bugs
- change pulse_duty to percentage of train_loop
- add input validation
- inverse output
- request_freq command
- finalize speed freq calculations
- A step cycle of over 100%, so we have phasing, the LEDs will overlap each other?
- Hardware simple and advanced outputs (pin) layout finalized.
- PC GUI

*/

#include < avr/io.h >
#include < avr/eeprom.h >
#include < avr/interrupt.h >
#include < avr/pgmspace.h >
#include < util/delay.h >
#include < LiquidCrystal.h >

// Global data
#define ZERO                     0
#define ONE                      1
#define PMCMDLIST_SIZE           30   // array size of commands.
#define PMLCDLIST_SIZE           15   // array size of lcd items.
#define CMD_BUFF_SIZE            30   // max cmd length
#define UNPSTR_BUFF_SIZE         80   // max string lenght
#define ADC_VALUE_MAX            1023 // 10bit adc

// PulseFire data
#define PULSE_FIRE_VERSION       04
#define PULSE_SERIAL_SPEED       115200
#define PULSE_OUTPUT_PORT        PORTB
#define PULSE_OUTPUT_DDR         DDRB
#define PULSE_DATA_OFF           0x00
#define PULSE_DATA_ON            0xFF

// External input pins
#define EXT_INPUT_TRIGGER_PIN    2    // External trigger like hall sensor.
#define EXT_INPUT_CLOCK_PIN      5    // Timer clock input for external clock source.
#define EXT_INPUT_PUSH0_PIN      3    // Human push botton trigger input pin.
#define EXT_INPUT_PUSH1_PIN      4    // cylcle over adc modes.
#define EXT_INPUT_ADC0_PIN       18   // train_wait.
#define EXT_INPUT_ADC1_PIN       19   // step_wait.

// Command parseing
#define CMD_MAX_ARGS             8
#define CMD_WHITE_SPACE          " \r\t\n"

// LCD pins
#define LCD_INIT_MSG_TIME        500  // Welcome message timeout
#define LCD_TEST_DOT_TIME        30   // 30ms per col test dot delay
#define LCD_SIZE_ROW             2    // 2 rows
#define LCD_SIZE_COL             20   // 20 columns
#define LCD_RS_PIN               6    // Arduino style pin numbers
#define LCD_E_PIN                7
#define LCD_D0_PIN               14
#define LCD_D1_PIN               15
#define LCD_D2_PIN               16
#define LCD_D3_PIN               17

// Startup defauts
#define DEFAULT_SYS_LCD_TIME     200        // Refresh lcd 5 times per second
#define DEFAULT_SYS_MENU_TIME    4000       // Exit menu after 4 seconds idle
#define DEFAULT_SYS_ADC_TIME     170        // Sample adc 10 times per second
#define DEFAULT_SYS_INPUT_TIME   5          // Read input 200 times a second.
#define DEFAULT_SYS_INPUT_DELAY  300        // Delay after input.
#define DEFAULT_SYS_START_DUTY   50         // Default starting duty for softstart
#define DEFAULT_PULSE_DATA_INIT  0x01       // Start with output 0 set to high
#define DEFAULT_PULSE_STEPS      5          // 5 outputs
#define DEFAULT_PULSE_SEQ_LENGTH 15         // 16bit seq bit data so 15 = max
#define DEFAULT_PULSE_SEQ_DATA   255        // 50% duty cycle
#define DEFAULT_TRAIN_LOOP       1000       // 10000
#define DEFAULT_TRAIN_CLOCK      1          // no prescale
#define DEFAULT_TRAIN_TCNT       65536-160  // 16Mhz/160 = 0.1Mhz clock pulse for timer
#define DEFAULT_STEP_LOOP        0          // Disable step duty
#define DEFAULT_STEP_CLOCK       1          // no prescale
#define DEFAULT_STEP_TCNT        256-160    // duty scale

// All enums, count as int 0,1,2,etc,etc
enum {PULSE_TRIGGER_LOOP,PULSE_TRIGGER_PUSH,PULSE_TRIGGER_EXT};
enum {TRAIN_STATE_IDLE,TRAIN_STATE_RUN,TRAIN_STATE_WAIT_LAST,TRAIN_STATE_WAIT_DUTY};
enum {STEP_STATE_IDLE,STEP_STATE_RUN,STEP_STATE_DONE};
enum {PULSE_MODE_OFF,PULSE_MODE_FLASH,PULSE_MODE_TRAIN,PULSE_MODE_TRAIN_REVERSE,PULSE_MODE_SEQUENCE};
enum {LCD_MENU_OFF, LCD_MENU_SELECT, LCD_MENU_VALUE};

const char pmGetSpaced[]         PROGMEM = "==";
const char pmSetSpaced[]         PROGMEM = "=";
const char pmPulseFire[]         PROGMEM = "PulseFire ";
const char pmPromt[]             PROGMEM = "root@pulsefire: ";
const char pmDone[]              PROGMEM = "done";
const char pmBootLine0[]         PROGMEM = "\r\n# ";
const char pmBootLine1[]         PROGMEM = "\r\n# Created for Magnagas/HHO researchers.\r\n";
const char pmLcdSelect[]         PROGMEM = "Select option ";
const char pmLcdSelectedOption[] PROGMEM = "O: ";
const char pmLcdMultiply[]       PROGMEM = " M:";
const char pmLcdValue[]          PROGMEM = "VALUE: ";
const char pmCmdHelpStart[]      PROGMEM = "# The commands are;\r\n";
const char pmCmdHelpEnd[]        PROGMEM = "# For argument help see source.";
const char pmCmdHelp[]           PROGMEM = "help";
const char pmCmdVersion[]        PROGMEM = "version";
const char pmCmdDefaults[]       PROGMEM = "defaults";
const char pmCmdSave[]           PROGMEM = "save";
const char pmCmdInfo[]           PROGMEM = "info";
const char pmCmdSysLcdTime[]     PROGMEM = "sys_lcd_time";
const char pmCmdSysMenuTime[]    PROGMEM = "sys_menu_time";
const char pmCmdSysAdcTime[]     PROGMEM = "sys_adc_time";
const char pmCmdSysAdcMap[]      PROGMEM = "sys_adc_map";
const char pmCmdSysInputTime[]   PROGMEM = "sys_input_time";
const char pmCmdSysInputDelay[]  PROGMEM = "sys_input_delay";
const char pmCmdSysStartSecs[]   PROGMEM = "sys_start_secs";
const char pmCmdSysStartDuty[]   PROGMEM = "sys_start_duty";
const char pmCmdPulseSteps[]     PROGMEM = "pulse_steps";
const char pmCmdPulseDuty[]      PROGMEM = "pulse_duty";
const char pmCmdPulseTrigger[]   PROGMEM = "pulse_trigger";
const char pmCmdPulseMode[]      PROGMEM = "pulse_mode";
const char pmCmdPulseInit[]      PROGMEM = "pulse_init";
const char pmCmdPulseSeqOff[]    PROGMEM = "pulse_seq_off";
const char pmCmdPulseSeqType[]   PROGMEM = "pulse_seq_type";
const char pmCmdPulseSeqData[]   PROGMEM = "pulse_seq_data";
const char pmCmdPulseSeqLength[] PROGMEM = "pulse_seq_len";
const char pmCmdTrainLoop[]      PROGMEM = "train_loop";
const char pmCmdTrainInc[]       PROGMEM = "train_inc";
const char pmCmdTrainTcnt[]      PROGMEM = "train_tcnt";
const char pmCmdTrainClock[]     PROGMEM = "train_clock";
const char pmCmdStepLoop[]       PROGMEM = "step_loop";
const char pmCmdStepInc[]        PROGMEM = "step_inc";
const char pmCmdStepTcnt[]       PROGMEM = "step_tcnt";
const char pmCmdStepClock[]      PROGMEM = "step_clock";
const char pmInfoCPUFreq[]       PROGMEM = "info_cpu";
const char pmInfoUptime[]        PROGMEM = "info_up";
const char pmPulseData[]         PROGMEM = "pulse_data";
const char pmStepLoopCnt[]       PROGMEM = "step_loop_cnt";
const char pmStepState[]         PROGMEM = "step_state";
const char pmTrainLoopCnt[]      PROGMEM = "train_loop_cnt";
const char pmTrainState[]        PROGMEM = "train_state";
const char pmCalcTrainSpeed[]    PROGMEM = "calc_train_speed";
const char pmCalcTrainLoop[]     PROGMEM = "calc_train_loop";
const char pmCalcTrainFreq[]     PROGMEM = "calc_train_freq";
const char pmCalcStepSpeed[]     PROGMEM = "calc_step_speed";
const char pmCalcStepLoop[]      PROGMEM = "calc_step_loop";
const char pmCalcStepFreqOn[]    PROGMEM = "calc_step_freq_on";
const char pmCalcStepFreqOff[]   PROGMEM = "calc_step_freq_off";

// Array of pointers to all commands.
PGM_P pmCmdList[PMCMDLIST_SIZE] PROGMEM = {
    pmCmdHelp,pmCmdInfo,pmCmdSave,pmCmdVersion,pmCmdDefaults,
    pmCmdSysLcdTime,pmCmdSysMenuTime,pmCmdSysAdcTime,pmCmdSysAdcMap,
    pmCmdSysInputTime,pmCmdSysInputDelay,
    pmCmdSysStartSecs,pmCmdSysStartDuty,
    pmCmdPulseSteps,pmCmdPulseDuty,pmCmdPulseTrigger,pmCmdPulseMode,pmCmdPulseInit,
    pmCmdPulseSeqOff,pmCmdPulseSeqType,pmCmdPulseSeqData,pmCmdPulseSeqLength,
    pmCmdTrainLoop,pmCmdTrainInc,pmCmdTrainTcnt,pmCmdTrainClock,
    pmCmdStepLoop,pmCmdStepInc,pmCmdStepTcnt,pmCmdStepClock
};

// Array of pointers to lcd menu.
PGM_P pmLcdList[PMLCDLIST_SIZE] PROGMEM = {
    pmCmdTrainLoop,pmCmdTrainInc,pmCmdTrainClock,
    pmCmdStepLoop,pmCmdStepInc,pmCmdStepClock,
    pmCmdPulseSteps,pmCmdPulseDuty,pmCmdPulseTrigger,pmCmdPulseMode,pmCmdPulseInit,
    pmCmdPulseSeqOff,pmCmdPulseSeqType,pmCmdPulseSeqData,
    pmCmdSave
};

// PulseFire internal config
typedef struct {
  volatile uint8_t  sys_version;
  volatile uint8_t  sys_struct_size;
  volatile uint16_t sys_lcd_time;
  volatile uint16_t sys_menu_time;
  volatile uint16_t sys_adc_time;
  volatile uint16_t sys_adc_map[6][3];
  volatile uint16_t sys_input_time;
  volatile uint16_t sys_input_delay;
  volatile uint16_t sys_start_secs;
  volatile uint16_t sys_start_duty;
 
  volatile uint16_t pulse_mode;
  volatile uint16_t pulse_data_init;
  volatile uint16_t pulse_steps;
  volatile uint16_t pulse_duty;
  volatile uint16_t pulse_trigger;
  volatile uint16_t pulse_seq_off;
  volatile uint16_t pulse_seq_type;
  volatile uint16_t pulse_seq_data;
  volatile uint16_t pulse_seq_length;
 
  volatile uint16_t train_loop_max_init;
  volatile uint16_t train_loop_max_inc;
  volatile uint16_t train_tcnt;
  volatile uint16_t train_clock;
 
  volatile uint16_t step_loop_max_init;
  volatile uint16_t step_loop_max_inc;
  volatile uint16_t step_tcnt;
  volatile uint16_t step_clock;
} pf_conf_struct;

// PulseFire internal data
typedef struct {
  unsigned long sys_lcd_time_cnt;
  unsigned long sys_menu_time_cnt;
  unsigned long sys_adc_time_cnt;
  unsigned long sys_input_time_cnt;
  volatile uint16_t sys_start_secs_cnt;
  volatile uint16_t sys_start_duty_cnt;

  volatile uint16_t pulse_data;
  volatile uint16_t pulse_duty_cnt;
  volatile uint16_t pulse_seq_cnt;
 
  volatile uint16_t train_state;
  volatile uint16_t train_loop_cnt;
  volatile uint16_t train_loop_max;
 
  volatile uint16_t step_state;
  volatile uint16_t step_loop_cnt;
  volatile uint16_t step_loop_max;
 
} pf_data_struct;

// Create stuctures
pf_data_struct       pf_data;
pf_conf_struct       pf_conf;
pf_conf_struct EEMEM pf_conf_eeprom;

// normal processing variables
char unpstr_buff[UNPSTR_BUFF_SIZE];
int  cmd_buff_idx  = ZERO;
char cmd_buff[CMD_BUFF_SIZE];
int  lcd_menu      = LCD_MENU_OFF;
int  lcd_menu_idx  = ZERO;
int  lcd_menu_mul  = ONE;

// Seqence output index for max 8 outs.
volatile uint8_t ppm_out_data[8] = {
  0,0,0,0,
  0,0,0,0
};

// Seqence data
volatile uint16_t ppm_seq_data[25] = {

  0b1000000000000000, // 93.75%  - 1
  0b1100000000000000,
  0b1110000000000000,
  0b1111000000000000,
  0b1111100000000000,
  0b1111110000000000,
  0b1111111000000000,
  0b1111111100000000, // 50% - 7
  0b1111111110000000,
  0b1111111111000000,
  0b1111111111100000,
  0b1111111111110000,
  0b1111111111111000,
  0b1111111111111100,
  0b1111111111111110, // 6.25%  - 14
 
  0b1010101010101010, // turbe mode 50 %
  0b1010101011111111,
  0b1010101111111111,

  0b1001001111111111, //
  0b1001001001111111,
  0b1001001001001111,
  0b1001001001001001,

  0b1000111111111111, //
  0b1000011100110111,
  0b1000001111100011
};

// init lcd object
LiquidCrystal lcd (
  LCD_RS_PIN,LCD_E_PIN,
  LCD_D0_PIN,LCD_D1_PIN,LCD_D2_PIN,LCD_D3_PIN
);


// Uncopy from program/flash memory to sram
char* UNPSTR(const char* dstring) {
  for (int i=0;i < UNPSTR_BUFF_SIZE;i++) {
    unpstr_buff[i]='\0'; // clean buffer
  }
  int index = 0;
  while (pgm_read_byte(dstring) != 0x00) {
    uint8_t c = pgm_read_byte(dstring++);
    unpstr_buff[index]=c;
    index++;
  }
  return unpstr_buff;
}

// Fill pstr_buff from pointer
char* UNPSTRA(const prog_char** argu) {
  for (int c=0;c < UNPSTR_BUFF_SIZE;c++) {
    unpstr_buff[c]='\0';
  }
  PGM_P p;
  memcpy_P(&p,argu, sizeof(PGM_P));
  strcpy_P(unpstr_buff, p);
  return unpstr_buff;
}

// Parser ascii to unsigned word.
uint16_t atou16(char* s) {
  uint16_t nn = 0;
  char* ss = s;
  while (*ss++) { nn++; }
  uint16_t num=0;
  for (int i=0;i <= nn;i++) {
    if (s[i] >= '0' && s[i] <= '9') {
      num = num * 10 + s[i] -'0';
    } else {
      break;
    }
  }
  return num;
}

// Init all config and data to init state.
void reset_config(void) {
  pf_conf.sys_version          = PULSE_FIRE_VERSION;
  pf_conf.sys_struct_size      = sizeof(pf_conf_struct);
  pf_conf.sys_lcd_time         = DEFAULT_SYS_LCD_TIME;
  pf_conf.sys_menu_time        = DEFAULT_SYS_MENU_TIME;
  pf_conf.sys_adc_time         = DEFAULT_SYS_ADC_TIME;
  for (int i=0;i < 6;i++) {
    pf_conf.sys_adc_map[i][0]  = ZERO;
    pf_conf.sys_adc_map[i][1]  = ZERO;
    pf_conf.sys_adc_map[i][2]  = ZERO;
  }
  pf_conf.sys_input_time       = DEFAULT_SYS_INPUT_TIME;
  pf_conf.sys_input_delay      = DEFAULT_SYS_INPUT_DELAY;
  pf_conf.sys_start_secs       = ZERO;
  pf_conf.sys_start_duty       = DEFAULT_SYS_START_DUTY;
  pf_conf.pulse_mode           = PULSE_MODE_TRAIN;
  pf_conf.pulse_data_init      = DEFAULT_PULSE_DATA_INIT;
  pf_conf.pulse_steps          = DEFAULT_PULSE_STEPS;
  pf_conf.pulse_duty           = ZERO;
  pf_conf.pulse_trigger        = PULSE_TRIGGER_LOOP;
  pf_conf.pulse_seq_off        = ZERO;
  pf_conf.pulse_seq_type       = ZERO;
  pf_conf.pulse_seq_data       = DEFAULT_PULSE_SEQ_DATA;
  pf_conf.pulse_seq_length     = DEFAULT_PULSE_SEQ_LENGTH;
  pf_conf.train_clock          = DEFAULT_TRAIN_CLOCK;
  pf_conf.train_loop_max_init  = DEFAULT_TRAIN_LOOP;
  pf_conf.train_loop_max_inc   = ZERO;
  pf_conf.train_tcnt           = DEFAULT_TRAIN_TCNT;
  pf_conf.step_clock           = DEFAULT_STEP_CLOCK;
  pf_conf.step_loop_max_init   = DEFAULT_STEP_LOOP;
  pf_conf.step_loop_max_inc    = ZERO;
  pf_conf.step_tcnt            = DEFAULT_STEP_TCNT;
}
void reset_data(void) {
  PULSE_OUTPUT_PORT            = PULSE_DATA_OFF;
  pf_data.sys_lcd_time_cnt     = ZERO;
  pf_data.sys_adc_time_cnt     = ZERO;
  pf_data.sys_input_time_cnt   = ZERO;
  pf_data.sys_start_secs_cnt   = ONE;  // is zero after startup.
  pf_data.sys_start_duty_cnt   = ZERO;
  pf_data.pulse_data           = pf_conf.pulse_data_init;
  pf_data.pulse_duty_cnt       = ZERO;
  pf_data.pulse_seq_cnt        = ZERO;
  pf_data.train_loop_cnt       = ZERO;
  pf_data.train_loop_max       = pf_conf.train_loop_max_init;
  if (pf_conf.pulse_trigger   == PULSE_TRIGGER_LOOP) {
    pf_data.train_state        = TRAIN_STATE_RUN;
  } else {
    pf_data.train_state        = TRAIN_STATE_IDLE;
  }
  pf_data.step_loop_cnt        = ZERO;
  pf_data.step_loop_max        = pf_conf.step_loop_max_init;
  pf_data.step_state           = STEP_STATE_IDLE;
 
  // also update default init data for easy reversal.
  if (pf_conf.pulse_mode      == PULSE_MODE_TRAIN_REVERSE && pf_conf.pulse_data_init == DEFAULT_PULSE_DATA_INIT ) {
    pf_conf.pulse_data_init    = (ONE << (pf_conf.pulse_steps - ONE));
  } else {
    pf_conf.pulse_data_init    = DEFAULT_PULSE_DATA_INIT;
  }
}


int convert_clock(int clockScaleMode) {
  int clockScale = 1;
  switch (clockScaleMode) {
    case 1:  clockScale = 1;    break;
    case 2:  clockScale = 8;    break;
    case 3:  clockScale = 64;   break;
    case 4:  clockScale = 256;  break;
    case 5:  clockScale = 1024; break;
    default: clockScale = 1;    break;
  }
  return clockScale;
}
int calc_outputs() {
  if (pf_conf.pulse_mode == PULSE_MODE_FLASH) {
    return 1;
  }
  return pf_conf.pulse_steps;
}
long calc_train_speed() {
  int clockScaleMode = TCCR1B; // todo mask 3 bit
  int clockScale = convert_clock(clockScaleMode);
  long freqTrain = F_CPU / clockScale / (65536-pf_conf.train_tcnt);
  return freqTrain;
}
long calc_step_speed() {
  int clockScaleMode = TCCR2B; // todo mask 3 bit
  int clockScale = convert_clock(clockScaleMode);
  long freqStep = F_CPU / clockScale / (256-pf_conf.step_tcnt);
  return freqStep;
}
long calc_train_loop() {
  if (pf_conf.train_loop_max_init==0) {
    return 0;
  }
  return calc_train_speed() / pf_conf.train_loop_max_init / 10 / calc_outputs() ;
}
long calc_step_loop() {
  if (pf_conf.step_loop_max_init==0) {
    return 0;
  }
  return calc_step_speed() / pf_conf.step_loop_max_init  / 10 / calc_outputs() ;
}
long calc_train_freq() {
  int trainStepLoop = calc_train_loop();
  int dutyStep = trainStepLoop * pf_data.pulse_duty_cnt;
  return ((trainStepLoop / calc_outputs()) + dutyStep) / 2;
}
long calc_step_freq_on() {
  return calc_train_loop() / 2;
}
long calc_step_freq_off() {
  return calc_step_loop() / 2;
}

void cmd_request_freq(void) {

}

void cmd_print_info_value(const char* dstring,uint16_t value) {
  Serial.print(UNPSTR(dstring));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(value);
  Serial.println();
}
void cmd_print_info_value_long(const char* dstring,long value) {
  Serial.print(UNPSTR(dstring));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(value);
  Serial.println();
}
void cmd_print_adc_map_value(int i) {
  Serial.print(i);
  Serial.print(' ');
  Serial.print(pf_conf.sys_adc_map[i][0]);
  Serial.print(' ');
  Serial.print(pf_conf.sys_adc_map[i][1]);
  Serial.print(' ');
  Serial.print(pf_conf.sys_adc_map[i][2]);
}
void cmd_print_info(void) {
  cmd_print_info_value(pmCmdSysLcdTime,        pf_conf.sys_lcd_time);
  cmd_print_info_value(pmCmdSysMenuTime,       pf_conf.sys_menu_time);
  cmd_print_info_value(pmCmdSysAdcTime,        pf_conf.sys_adc_time);
  for (int i=0;i < 6;i++) {
    Serial.print(UNPSTR(pmCmdSysAdcMap));
    Serial.print(i);
    Serial.print(UNPSTR(pmGetSpaced));
    cmd_print_adc_map_value(i);
    Serial.println();
  }
  cmd_print_info_value(pmCmdSysInputTime,      pf_conf.sys_input_time);
  cmd_print_info_value(pmCmdSysInputDelay,     pf_conf.sys_input_delay);
  cmd_print_info_value(pmCmdSysStartSecs,      pf_conf.sys_start_secs);
  cmd_print_info_value(pmCmdSysStartDuty,      pf_conf.sys_start_duty);
  cmd_print_info_value(pmCmdPulseSteps,        pf_conf.pulse_steps);
  cmd_print_info_value(pmCmdPulseDuty,         pf_conf.pulse_duty);
  cmd_print_info_value(pmCmdPulseTrigger,      pf_conf.pulse_trigger);
  cmd_print_info_value(pmCmdPulseMode,         pf_conf.pulse_mode);    
  cmd_print_info_value(pmCmdPulseInit,         pf_conf.pulse_data_init);
  cmd_print_info_value(pmCmdPulseSeqOff,       pf_conf.pulse_seq_off);
  cmd_print_info_value(pmCmdPulseSeqType,      pf_conf.pulse_seq_type);
  cmd_print_info_value(pmCmdPulseSeqData,      pf_conf.pulse_seq_data);
  cmd_print_info_value(pmCmdPulseSeqLength,    pf_conf.pulse_seq_length);
  cmd_print_info_value(pmCmdTrainLoop,         pf_conf.train_loop_max_init);
  cmd_print_info_value(pmCmdTrainInc,          pf_conf.train_loop_max_inc);
  cmd_print_info_value(pmCmdTrainTcnt,         pf_conf.train_tcnt);
  cmd_print_info_value(pmCmdTrainClock,        pf_conf.train_clock);
  cmd_print_info_value(pmCmdStepLoop,          pf_conf.step_loop_max_init);
  cmd_print_info_value(pmCmdStepInc,           pf_conf.step_loop_max_inc);
  cmd_print_info_value(pmCmdStepTcnt,          pf_conf.step_tcnt);
  cmd_print_info_value(pmCmdStepClock,         pf_conf.step_clock);
  cmd_print_info_value(pmPulseData,            pf_data.pulse_data);  
  cmd_print_info_value(pmTrainLoopCnt,         pf_data.train_loop_cnt);
  cmd_print_info_value(pmTrainState,           pf_data.train_state);
  cmd_print_info_value(pmStepLoopCnt,          pf_data.step_loop_cnt);
  cmd_print_info_value(pmStepState,            pf_data.step_state);
  cmd_print_info_value_long(pmCalcTrainSpeed,  calc_train_speed());
  cmd_print_info_value_long(pmCalcTrainLoop,   calc_train_loop());
  cmd_print_info_value_long(pmCalcTrainFreq,   calc_train_freq());
  cmd_print_info_value_long(pmCalcStepSpeed,   calc_step_speed());
  cmd_print_info_value_long(pmCalcStepLoop,    calc_step_loop());
  cmd_print_info_value_long(pmCalcStepFreqOn,  calc_step_freq_on());
  cmd_print_info_value_long(pmCalcStepFreqOff, calc_step_freq_off());
  cmd_print_info_value_long(pmInfoUptime,      millis());
  cmd_print_info_value_long(pmInfoCPUFreq,     F_CPU);
  Serial.print(UNPSTR(pmCmdInfo));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(UNPSTR(pmDone));
}

// Check and parse command
uint16_t cmd_property(char* cmd, char** args,const char* checkCmd,uint16_t value) {
  if (strcmp_P(cmd,checkCmd) != ZERO) {
    return value;
  }
  Serial.print(UNPSTR(checkCmd));
  if (args[0] == NULL) {
    Serial.print(UNPSTR(pmGetSpaced));
  } else {
    Serial.print(UNPSTR(pmSetSpaced));
    value = atou16(args[0]);
  }
  Serial.print(value);
  return value;
}

// execute cmd with the supplied argument
void cmd_execute(char* cmd, char** args) {
  if ( strcmp_P(cmd,pmCmdHelp) == ZERO ) {
    Serial.print(UNPSTR(pmCmdHelpStart));
    for (int i=0;i < PMCMDLIST_SIZE;i++) {
      Serial.print(UNPSTRA(&pmCmdList[i]));
      Serial.println();
    }
    Serial.print(UNPSTR(pmCmdHelpEnd));
    return;
  } else if (strcmp_P(cmd,pmCmdVersion) == ZERO) {
    Serial.print(UNPSTR(pmCmdVersion));
    Serial.print(UNPSTR(pmGetSpaced));
    Serial.print(PULSE_FIRE_VERSION/10 % 10);
    Serial.print('.');
    Serial.print(PULSE_FIRE_VERSION % 10);
    return;
  } else if (strcmp_P(cmd,pmCmdInfo) == ZERO) {
    cmd_print_info();
    return;
  } else if (strcmp_P(cmd,pmCmdDefaults) == ZERO) {
    Serial.print(UNPSTR(pmCmdDefaults));
    reset_config();
    reset_data();
    Serial.print(UNPSTR(pmSetSpaced));
    Serial.print(ONE);
    return;
  } else if (strcmp_P(cmd,pmCmdSave) == ZERO) {
    Serial.print(UNPSTR(pmCmdSave));
    eeprom_write_block((const void*)&pf_conf,(void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
    Serial.print(UNPSTR(pmSetSpaced));
    Serial.print(ONE);
    return;
  } else if (strcmp_P(cmd,pmCmdSysAdcMap) == ZERO) {
    Serial.print(UNPSTR(pmCmdSysAdcMap));
    if (args[0] == NULL) {
      Serial.print(UNPSTR(pmGetSpaced));
      Serial.print(ZERO);
      return;
    }
    uint16_t adc_idx = atou16(args[0]);
    if (args[1] == NULL) {
      Serial.print(UNPSTR(pmGetSpaced));
      cmd_print_adc_map_value(adc_idx);
      return;
    }
    uint16_t adc_map_to  = atou16(args[1]);
    uint16_t adc_map_min = ZERO;
    uint16_t adc_map_max = 65535;
    if (args[2] != NULL) { adc_map_min = atou16(args[2]); }    
    if (args[3] != NULL) { adc_map_max = atou16(args[3]); }    
    pf_conf.sys_adc_map[adc_idx][0] = adc_map_to;
    pf_conf.sys_adc_map[adc_idx][1] = adc_map_min;
    pf_conf.sys_adc_map[adc_idx][2] = adc_map_max;
    Serial.print(UNPSTR(pmSetSpaced));
    cmd_print_adc_map_value(adc_idx);
    return;
  }
 
  // process all get/set properties
  pf_conf.sys_lcd_time        = cmd_property(cmd,args,pmCmdSysLcdTime,    pf_conf.sys_lcd_time);
  pf_conf.sys_menu_time       = cmd_property(cmd,args,pmCmdSysMenuTime,   pf_conf.sys_menu_time);
  pf_conf.sys_adc_time        = cmd_property(cmd,args,pmCmdSysAdcTime,    pf_conf.sys_adc_time);
  pf_conf.sys_input_time      = cmd_property(cmd,args,pmCmdSysInputTime,  pf_conf.sys_input_time);
  pf_conf.sys_input_delay     = cmd_property(cmd,args,pmCmdSysInputDelay, pf_conf.sys_input_delay);
  pf_conf.sys_start_secs      = cmd_property(cmd,args,pmCmdSysStartSecs,  pf_conf.sys_start_secs);
  pf_conf.sys_start_duty      = cmd_property(cmd,args,pmCmdSysStartDuty,  pf_conf.sys_start_duty);
  pf_conf.pulse_steps         = cmd_property(cmd,args,pmCmdPulseSteps,    pf_conf.pulse_steps);
  pf_conf.pulse_duty          = cmd_property(cmd,args,pmCmdPulseDuty,     pf_conf.pulse_duty);
  pf_conf.pulse_trigger       = cmd_property(cmd,args,pmCmdPulseTrigger,  pf_conf.pulse_trigger);
  pf_conf.pulse_mode          = cmd_property(cmd,args,pmCmdPulseMode,     pf_conf.pulse_mode);
  pf_conf.pulse_data_init     = cmd_property(cmd,args,pmCmdPulseInit,     pf_conf.pulse_data_init);
  pf_conf.pulse_seq_off       = cmd_property(cmd,args,pmCmdPulseSeqOff,   pf_conf.pulse_seq_off);
  pf_conf.pulse_seq_type      = cmd_property(cmd,args,pmCmdPulseSeqType,  pf_conf.pulse_seq_type);
  pf_conf.pulse_seq_data      = cmd_property(cmd,args,pmCmdPulseSeqData,  pf_conf.pulse_seq_data);
  pf_conf.pulse_seq_length    = cmd_property(cmd,args,pmCmdPulseSeqLength,pf_conf.pulse_seq_length);
  pf_conf.train_loop_max_init = cmd_property(cmd,args,pmCmdTrainLoop,     pf_conf.train_loop_max_init);
  pf_conf.train_loop_max_inc  = cmd_property(cmd,args,pmCmdTrainInc,      pf_conf.train_loop_max_inc);
  pf_conf.train_tcnt          = cmd_property(cmd,args,pmCmdTrainTcnt,     pf_conf.train_tcnt);
  pf_conf.train_clock         = cmd_property(cmd,args,pmCmdTrainClock,    pf_conf.train_clock);
  pf_conf.step_loop_max_init  = cmd_property(cmd,args,pmCmdStepLoop,      pf_conf.step_loop_max_init);
  pf_conf.step_loop_max_inc   = cmd_property(cmd,args,pmCmdStepInc,       pf_conf.step_loop_max_inc);
  pf_conf.step_tcnt           = cmd_property(cmd,args,pmCmdStepTcnt,      pf_conf.step_tcnt);
  pf_conf.step_clock          = cmd_property(cmd,args,pmCmdStepClock,     pf_conf.step_clock);
   
  // Correct overflow, 0 = overflow value, so gives interrupt loop.
  if (pf_conf.train_tcnt == ZERO) {
    pf_conf.train_tcnt=ONE;
  }
  if (pf_conf.step_tcnt == ZERO) {
    pf_conf.step_tcnt=ONE;
  }

  // Save updated clock value to registers
  if (strcmp_P(cmd,pmCmdTrainClock) == ZERO && args[0] != NULL) {
    TCCR1B = pf_conf.train_clock; // no masking...
  }
  if (strcmp_P(cmd,pmCmdStepClock) == ZERO && args[0] != NULL) {
    TCCR2B = pf_conf.step_clock; // no masking...    
  }

  // reset data when trigger or mode is changed
  if (strcmp_P(cmd,pmCmdPulseTrigger) == ZERO && args[0] != NULL) {
    reset_data();
  }
  if (strcmp_P(cmd,pmCmdPulseMode) == ZERO && args[0] != NULL) {
    reset_data();
  }
}

// Parse the cmd from the cmd_buff
void cmd_parse(void) {
  int idx = 0;
  char *cmd, *ptr, *args[CMD_MAX_ARGS];
  if (strtok((char *)cmd_buff,CMD_WHITE_SPACE) == NULL) {
    Serial.print(UNPSTR(pmPromt));
    return; // no command given so just print new promt.
  }
  cmd = (char *)cmd_buff;
  while( (ptr = strtok(NULL,CMD_WHITE_SPACE)) != NULL) {
    args[idx] = ptr;
    if (++idx == (CMD_MAX_ARGS-1)) {
      break;
    }
  }
  args[idx] = NULL;
  cmd_execute(cmd,args);
  Serial.println();
  Serial.print(UNPSTR(pmPromt));
}

// Check for data from console and put in cmd_buff
void loop_serial(void) {
  int readBytes = Serial.available();
  if (readBytes==ZERO) {
    return;
  }
  boolean cmd_process = false;
  for (int b=ZERO;b < readBytes;b++) {
    int c = Serial.read();
    Serial.write(c);
    if (cmd_buff_idx>CMD_BUFF_SIZE) {
      cmd_buff_idx = ZERO; // protect against to long input
    }
    if (c=='\b') {
      cmd_buff[cmd_buff_idx] = '\0';// backspace
      cmd_buff_idx--;
      Serial.write(' ');
      Serial.write(c);
    } else if (c=='\n') {
      cmd_buff[cmd_buff_idx] = '\0';// newline
      cmd_process=true;
      cmd_buff_idx=ZERO;
    } else {
      cmd_buff[cmd_buff_idx] = c;   // store in buffer
      cmd_buff_idx++;
    }
  }
  if (cmd_process == true) {
     cmd_parse();
  }
}

// process/print or print value only of selected menu items
void loop_input_menu_item(boolean count_down,boolean print_only,boolean print_header) {
  if (print_header) {
    lcd.clear();
    lcd.print(UNPSTR(pmLcdSelectedOption));
    lcd.print(UNPSTRA(&pmLcdList[lcd_menu_idx]));
    lcd.print(UNPSTR(pmLcdMultiply));
    lcd.print(lcd_menu_mul);
    lcd.setCursor(0,1);
    lcd.print(UNPSTR(pmLcdValue));
  }
  int m = lcd_menu_mul;
  int v = lcd_menu_idx;
  if (count_down) {
    v = v+100; // default is up, so this is down.
  }
  if (print_only) {
    if (count_down) {
      v = v+100;
    } else {
      v = v+200;
    }
  }
  switch(v) {
    case 0:   pf_conf.train_loop_max_init+=2*m;
    case 100: pf_conf.train_loop_max_init-=m;
    case 200: lcd.print(pf_conf.train_loop_max_init); break;
    case 1:   pf_conf.train_loop_max_inc+=2*m;
    case 101: pf_conf.train_loop_max_inc-=m;
    case 201: lcd.print(pf_conf.train_loop_max_inc); break;
    case 2:   pf_conf.train_clock+=2*m;
    case 102: pf_conf.train_clock-=m;
    case 202: lcd.print(pf_conf.train_clock); break;
    case 3:   pf_conf.step_loop_max_init+=2*m;
    case 103: pf_conf.step_loop_max_init-=m;
    case 203: lcd.print(pf_conf.step_loop_max_init); break;
    case 4:   pf_conf.step_loop_max_inc+=2*m;
    case 104: pf_conf.step_loop_max_inc-=m;
    case 204: lcd.print(pf_conf.step_loop_max_inc); break;
    case 5:   pf_conf.step_clock+=2*m;
    case 105: pf_conf.step_clock-=m;
    case 205: lcd.print(pf_conf.step_clock); break;
    case 6:   pf_conf.pulse_steps+=2*m;
    case 106: pf_conf.pulse_steps-=m;
    case 206: lcd.print(pf_conf.pulse_steps); break;
    case 7:   pf_conf.pulse_duty+=2*m;
    case 107: pf_conf.pulse_duty-=m;
    case 207: lcd.print(pf_conf.pulse_duty); break;
    case 8:   pf_conf.pulse_trigger+=2*m;
    case 108: pf_conf.pulse_trigger-=m;reset_data();
    case 208: lcd.print(pf_conf.pulse_trigger); break;
    case 9:   pf_conf.pulse_mode+=2*m;
    case 109: pf_conf.pulse_mode-=m;reset_data();
    case 209: lcd.print(pf_conf.pulse_mode); break;
    case 10:  pf_conf.pulse_data_init+=2*m;
    case 110: pf_conf.pulse_data_init-=m;
    case 210: lcd.print(pf_conf.pulse_data_init); break;
    case 11:  pf_conf.pulse_seq_off+=2*m;
    case 111: pf_conf.pulse_seq_off-=m;
    case 211: lcd.print(pf_conf.pulse_seq_off); break;
    case 12:  pf_conf.pulse_seq_type+=2*m;
    case 112: pf_conf.pulse_seq_type-=m;
    case 212: lcd.print(pf_conf.pulse_seq_type); break;
    case 13:  pf_conf.pulse_seq_data+=2*m;
    case 113: pf_conf.pulse_seq_data-=m;
    case 213: lcd.print(pf_conf.pulse_seq_data); break;
    case 14:
    case 114:
    case 214:
          eeprom_write_block((const void*)&pf_conf,(void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
          lcd.print(UNPSTR(pmDone));
          break;
    default:
          break;
  }  
}

// Check for user input via buttons.
void loop_input(unsigned long current_time) {
  if (current_time < pf_data.sys_input_time_cnt) {
    return;
  }
  pf_data.sys_input_time_cnt = current_time + pf_conf.sys_input_time;
 
  int input0 = digitalRead(EXT_INPUT_PUSH0_PIN);
  int input1 = digitalRead(EXT_INPUT_PUSH1_PIN);
  if (lcd_menu != LCD_MENU_OFF && input0 == HIGH && input1 == HIGH) {
    if (current_time < pf_data.sys_menu_time_cnt) {
      return; // no input to process
    }
    lcd_menu     = LCD_MENU_OFF;
    lcd_menu_idx = ZERO;
    lcd_menu_mul = ONE;
    return; // exit menu after idle counter timeout
  }
  pf_data.sys_menu_time_cnt = current_time + pf_conf.sys_menu_time;
 
  if (lcd_menu == LCD_MENU_OFF) {
    if (input0 == LOW) {
      lcd_menu     = LCD_MENU_SELECT;
      lcd_menu_idx = ZERO;
    }
    if (input1 == LOW) {
      if (pf_conf.pulse_trigger != PULSE_TRIGGER_PUSH) {
        return; // we only check for button if it is selected as source
      }
      if (pf_data.train_state == TRAIN_STATE_RUN) {
        return; // we just fired it so pulse train is still/has running.
      }
      pf_data.pulse_data  = pf_conf.pulse_data_init;
      pf_data.train_state = TRAIN_STATE_RUN;
    }
  } else if (lcd_menu == LCD_MENU_SELECT) {
    if (input0 == LOW) {      
      if (lcd_menu_idx == PMLCDLIST_SIZE) {
        lcd_menu_idx = ZERO;
      }
      lcd.clear();
      lcd.print(UNPSTR(pmLcdSelect));
      lcd.setCursor(ZERO,ONE);
      lcd.print(UNPSTRA(&pmLcdList[lcd_menu_idx]));
      lcd.setCursor(LCD_SIZE_COL-5,ONE); // have 5 chars for value
      loop_input_menu_item(false,true,false);
      lcd_menu_idx++;
    }
    if (input1 == LOW) {
      lcd_menu_idx--; // go to selected value
      lcd_menu=LCD_MENU_VALUE;
      loop_input_menu_item(true,true,true);
    }
  } else if (lcd_menu == LCD_MENU_VALUE) {
    if (input0 == LOW && input1 == LOW) {
      lcd_menu_mul = lcd_menu_mul * 10;  // make up&down 10faster
    }
    loop_input_menu_item(input1==LOW,false,true);
  }
  _delay_ms(pf_conf.sys_input_delay);
}

// read out analog values.
void loop_input_adc(unsigned long current_time) {
  if (current_time < pf_data.sys_adc_time_cnt) {
    return;
  }
  pf_data.sys_adc_time_cnt = current_time + pf_conf.sys_adc_time;

  boolean output_mode = true;  
  int pin_idx = 14;
  for (int i=0;i < 6;i++) {
    if (output_mode == true) {
      if (i < 4) {
         pin_idx++;
         continue; // only read 4 & 5 for now, until out_mode is there
      }
    }
    int valueAdc = analogRead(pin_idx);
    pin_idx++;
    if (pf_conf.sys_adc_map[i][0] == ZERO) {
      continue; // no mapping
    }
    // map to normal and assign to value
    valueAdc = map(valueAdc,ZERO,ADC_VALUE_MAX,pf_conf.sys_adc_map[i][1],pf_conf.sys_adc_map[i][2]);
    switch (pf_conf.sys_adc_map[i][0]) {
      case  1:pf_conf.train_loop_max_init = valueAdc; break;
      case  2:pf_conf.train_loop_max_inc  = valueAdc; break;
      case  3:pf_conf.train_tcnt          = valueAdc; break;
      case  4:pf_conf.train_clock         = valueAdc; break;
      case  5:pf_conf.step_loop_max_init  = valueAdc; break;
      case  6:pf_conf.step_loop_max_inc   = valueAdc; break;
      case  7:pf_conf.step_tcnt           = valueAdc; break;
      case  8:pf_conf.step_clock          = valueAdc; break;
      case  9:pf_conf.pulse_duty          = valueAdc; break;
      case 10:pf_conf.pulse_mode          = valueAdc; break;
      case 11:pf_conf.pulse_trigger       = valueAdc; break;
      case 12:pf_conf.pulse_seq_off       = valueAdc; break;
      case 13:pf_conf.pulse_seq_type      = valueAdc; break;
      case 14:pf_conf.pulse_seq_data      = valueAdc; break;
      default: break;
    }
  }
}

// refesh the lcd
void loop_lcd(unsigned long current_time) {
  if (current_time < pf_data.sys_lcd_time_cnt) {
    return; // wait until refresh
  }
  pf_data.sys_lcd_time_cnt = current_time + pf_conf.sys_lcd_time;
  if (lcd_menu != LCD_MENU_OFF) {
    return; // we are in menu mode.
  }
  lcd.clear();
  lcd.print("tr: ");
  lcd.print(pf_conf.train_loop_max_init);
  lcd.setCursor(10,0);
  lcd.print("hz: ");
  lcd.print(calc_train_freq());
  lcd.setCursor(ZERO,ONE);
  lcd.print("so: ");
  lcd.print(calc_step_freq_on());
  lcd.setCursor(10,1);
  lcd.print("sf: ");
  lcd.print(calc_step_freq_off());
}

void setup_config(void) {
  uint8_t pfVersion = eeprom_read_byte((uint8_t*)ZERO);
  uint8_t pfStructSize = eeprom_read_byte((uint8_t*)ONE);
  if (pfVersion==PULSE_FIRE_VERSION && pfStructSize==sizeof(pf_conf_struct)) {
    eeprom_read_block((void*)&pf_conf,(const void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
  } else {
    reset_config(); // if newer/other/none version&size in flash then
    reset_data();   // reset all to defaults and save.
    eeprom_write_block((const void*)&pf_conf,(void*)&pf_conf_eeprom,sizeof(pf_conf_struct));
  }
  reset_data();
}

void setup_input(void) {
  // external trigger pin
  EIMSK |= (1 << INT0);   // Enable INT0 External Interrupt
  MCUCR |= (1 << ISC01);  // Falling-Edge Triggered INT0
  pinMode      (EXT_INPUT_TRIGGER_PIN,INPUT);
  digitalWrite (EXT_INPUT_TRIGGER_PIN,HIGH);

  // clk source pin
  pinMode      (EXT_INPUT_CLOCK_PIN,INPUT);
  digitalWrite (EXT_INPUT_CLOCK_PIN,HIGH);

  // UI buttons
  pinMode      (EXT_INPUT_PUSH0_PIN,INPUT);
  digitalWrite (EXT_INPUT_PUSH0_PIN,HIGH);
  pinMode      (EXT_INPUT_PUSH1_PIN,INPUT);
  digitalWrite (EXT_INPUT_PUSH1_PIN,HIGH);
}

void setup_output(void) {
  PULSE_OUTPUT_DDR  = 0xFF;
  PULSE_OUTPUT_PORT = PULSE_DATA_OFF;
}

void setup_serial(void) {
  Serial.begin(PULSE_SERIAL_SPEED);
  Serial.print(UNPSTR(pmBootLine0));
  Serial.print(UNPSTR(pmPulseFire));
  Serial.print(UNPSTR(pmCmdVersion));
  Serial.print(UNPSTR(pmGetSpaced));
  Serial.print(PULSE_FIRE_VERSION/10 % 10);
  Serial.print('.');
  Serial.print(PULSE_FIRE_VERSION % 10);
  Serial.print(UNPSTR(pmBootLine1));
  Serial.print(UNPSTR(pmPromt));
}

void setup_timers(void) {
  // 16bit timer used for pulse steps.
  TCCR1B = DEFAULT_TRAIN_CLOCK;// start timer without prescaler
  TIMSK1|= (ONE << TOIE1);     // enable overflow int
  TCNT1  = ZERO;               // soft start
 
  // 8bit timer used for pulse step duty
  TCCR2B = DEFAULT_STEP_CLOCK;
  TIMSK2|= (ONE << TOIE2);
  TCNT2  = ZERO;
}

void setup_lcd(void) {
  lcd.begin(LCD_SIZE_COL, LCD_SIZE_ROW);
  lcd.print(UNPSTR(pmPulseFire));
  lcd.setCursor(0,1);
  lcd.print(UNPSTR(pmCmdVersion));
  lcd.print(' ');
  lcd.print(PULSE_FIRE_VERSION/10 % 10);
  lcd.print('.');
  lcd.print(PULSE_FIRE_VERSION % 10);
  _delay_ms(LCD_INIT_MSG_TIME);
  for (int i=0;i <= LCD_SIZE_COL;i++) {
    lcd.setCursor(LCD_SIZE_COL-i,0);
    lcd.write('.');
    lcd.setCursor(i,1);
    lcd.write('.');
    _delay_ms(LCD_TEST_DOT_TIME);
  }
  for (int i=0;i <= LCD_SIZE_COL;i++) {
    lcd.setCursor(LCD_SIZE_COL-i,0);
    lcd.write(' ');
    lcd.setCursor(i,1);
    lcd.write(' ');
    _delay_ms(LCD_TEST_DOT_TIME);
  }
  _delay_ms(LCD_TEST_DOT_TIME*5);
}

void int_soft_start(void) {
  long sys_up_secs = millis()/1000;
  if (sys_up_secs == ZERO) {
    sys_up_secs = ONE; // very fast cpu here.
  }
  pf_data.sys_start_secs_cnt = sys_up_secs;
  if (pf_data.sys_start_secs_cnt > pf_conf.sys_start_secs) {
    pf_data.sys_start_secs_cnt = ZERO; // we are done with startup.
  }
  int duty_step = (pf_conf.sys_start_duty - pf_conf.pulse_duty) / pf_conf.sys_start_secs;
  if (duty_step==ZERO) {
    duty_step = ONE; // some value else give no change
  }
  int startup_duty = pf_conf.sys_start_duty - (pf_data.sys_start_secs_cnt * duty_step);
  if (startup_duty < ZERO) {
    startup_duty = ONE; // make sure wait does not loop.
  }
  if (pf_data.pulse_data == pf_conf.pulse_data_init) {
    PULSE_OUTPUT_PORT = PULSE_DATA_OFF;
    pf_data.pulse_duty_cnt++;
    if (pf_data.pulse_duty_cnt < startup_duty) {
      return;
    }
    pf_data.pulse_duty_cnt = ZERO;
    pf_data.train_state    = TRAIN_STATE_IDLE;
  }
}
void int_pulse_step(void) {
  if (pf_data.step_state == STEP_STATE_RUN) {
    return;
  }
  if (pf_data.step_state == STEP_STATE_IDLE) {
    PULSE_OUTPUT_PORT  = PULSE_DATA_OFF; // Switch all off while waiting..
    pf_data.step_state = STEP_STATE_RUN; // start run
    TCNT2              = 255-3;          // run asp
    return;
  }
  pf_data.step_state = STEP_STATE_IDLE; // done with step duty.
}
void int_pulse_mode_flash(void) {
  if (pf_data.pulse_data == PULSE_DATA_ON) {
    pf_data.pulse_data = PULSE_DATA_OFF;
  } else {
    pf_data.pulse_data = PULSE_DATA_ON;
  }
}
void int_pulse_mode_seq(void) {
  uint16_t seq_data = 0;
  if (pf_conf.pulse_seq_type == ZERO) {
    seq_data = pf_conf.pulse_seq_data;
  } else {
    seq_data = ppm_seq_data[pf_conf.pulse_seq_type];
  }
  uint8_t out_data = 0;
  int out = pf_data.pulse_seq_cnt;
  uint8_t out_bit = (seq_data >> ppm_out_data[out]) & ONE;
  out_data |= (out_bit << out);
  if (ppm_out_data[out] == ZERO) {
    ppm_out_data[out] = pf_conf.pulse_seq_length;
    pf_data.pulse_seq_cnt++; // goto next step
  } else {
    ppm_out_data[out]--;
  }
  if (pf_conf.pulse_seq_off > 0 && pf_conf.pulse_seq_off > ppm_out_data[out]) {
    out = pf_data.pulse_seq_cnt + ONE;
    uint8_t out_bit = (seq_data >> ppm_out_data[out]) & ONE;
    out_data |= (out_bit << out);
    if (ppm_out_data[out] == ZERO) {
      ppm_out_data[out] = pf_conf.pulse_seq_length;
    } else {
      ppm_out_data[out]--;
    }
  }
  pf_data.pulse_data = out_data;
  if (pf_data.pulse_seq_cnt > (pf_conf.pulse_steps - ONE)) {
    pf_data.pulse_seq_cnt  = ZERO;
    pf_data.pulse_data     = PULSE_DATA_OFF;
    if (pf_conf.pulse_duty > ZERO) {
      pf_data.train_state  = TRAIN_STATE_WAIT_DUTY;
    }
    if (pf_conf.pulse_trigger > ZERO) {
      pf_data.train_state  = TRAIN_STATE_WAIT_LAST;
    }
  }
}

ISR(INT0_vect) {
  if (pf_conf.pulse_trigger == PULSE_TRIGGER_EXT) {
    pf_data.pulse_data  = pf_conf.pulse_data_init;
    pf_data.train_state = TRAIN_STATE_RUN;
  }
}

ISR(TIMER1_OVF_vect) {
  // set timer counter to start value so we can make timed nicely
  TCNT1 = pf_conf.train_tcnt;
 
  if (pf_conf.pulse_trigger != PULSE_TRIGGER_LOOP && pf_data.train_state == TRAIN_STATE_IDLE) {
    return;// disable when in manuale trigger fire.
  }
   
  // time step with counter
  pf_data.train_loop_cnt++;
  if (pf_data.train_loop_cnt < pf_data.train_loop_max) {
    return;
  }
  pf_data.train_loop_cnt = ZERO;

  // Check for soft startup
  if (pf_data.sys_start_secs_cnt > ZERO && pf_conf.sys_start_secs > ZERO) {
    int_soft_start();
  }

  // Check for offtime check wait
  if (pf_data.step_loop_max > ZERO) {
    int_pulse_step();
  }
 
  // use - for letting last output time correctly until off.
  if (pf_data.train_state == TRAIN_STATE_WAIT_LAST) {
    PULSE_OUTPUT_PORT      = PULSE_DATA_OFF;
    pf_data.train_state    = TRAIN_STATE_IDLE;
    return;
  }
  if (pf_data.train_state == TRAIN_STATE_WAIT_DUTY) {
    PULSE_OUTPUT_PORT      = PULSE_DATA_OFF;
    pf_data.pulse_duty_cnt++;
    if (pf_data.pulse_duty_cnt < pf_conf.pulse_duty) {
      return;
    }
    pf_data.pulse_duty_cnt = ZERO;
    pf_data.train_state    = TRAIN_STATE_IDLE;
  }

  // Set data to outputs
  PULSE_OUTPUT_PORT = pf_data.pulse_data;
 
  // speed up after each step
  if (pf_conf.train_loop_max_inc > ZERO && pf_data.train_loop_max > pf_conf.train_loop_max_inc) {
    pf_data.train_loop_max = pf_data.train_loop_max-pf_conf.train_loop_max_inc;
  }
  if (pf_data.pulse_data == pf_conf.pulse_data_init) {
    pf_data.train_loop_max = pf_conf.train_loop_max_init;
  }
 
  // Change pulse_data to next postition defined by pulse_mode
  switch (pf_conf.pulse_mode) {
    case PULSE_MODE_TRAIN:         pf_data.pulse_data = pf_data.pulse_data << ONE; break;
    case PULSE_MODE_TRAIN_REVERSE: pf_data.pulse_data = pf_data.pulse_data >> ONE; break;
    case PULSE_MODE_FLASH:         int_pulse_mode_flash(); break;
    case PULSE_MODE_SEQUENCE:      int_pulse_mode_seq();   break;
    case PULSE_MODE_OFF:
    default:                       pf_data.pulse_data = PULSE_DATA_OFF; break;
  }
   
  // check for output rotation, todo change to shorter
  if (pf_conf.pulse_mode == PULSE_MODE_TRAIN) {
    if (pf_data.pulse_data > (ONE << (pf_conf.pulse_steps - ONE))) {
      pf_data.pulse_data     = pf_conf.pulse_data_init;
      if (pf_conf.pulse_duty > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_DUTY;
      }
      if (pf_conf.pulse_trigger > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_LAST;
      }
    }
  } else if (pf_conf.pulse_mode == PULSE_MODE_TRAIN_REVERSE) {
    if (pf_data.pulse_data == ZERO) {
      pf_data.pulse_data     = pf_conf.pulse_data_init;
      if (pf_conf.pulse_duty > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_DUTY;
      }
      if (pf_conf.pulse_trigger > ZERO) {
        pf_data.train_state  = TRAIN_STATE_WAIT_LAST;
      }
    }
  }
}

ISR(TIMER2_OVF_vect) {  
  TCNT2 = pf_conf.step_tcnt;
  if (pf_data.step_state != STEP_STATE_RUN) {
    return;
  }
  pf_data.step_loop_cnt++;
  if (pf_data.step_loop_cnt < pf_data.step_loop_max) {
    return;
  }
  if (pf_conf.step_loop_max_inc > ZERO && pf_data.step_loop_max > pf_conf.step_loop_max_inc) {
    pf_data.step_loop_max = pf_data.step_loop_max-pf_conf.step_loop_max_inc;
  }
  if (pf_data.pulse_data == pf_conf.pulse_data_init) {
    pf_data.step_loop_max = pf_conf.step_loop_max_init;
  }
  pf_data.step_loop_cnt  = ZERO;              // reset counter for next run.
  pf_data.step_state     = STEP_STATE_DONE;   // reset state to next step
  pf_data.train_loop_cnt = pf_data.train_loop_max; // skip next step delay
  TCNT1                  = 65535-3;           // run asap next step
}

void setup() {
  setup_config();
  setup_input();
  setup_output();
  setup_timers();
  setup_serial();
  setup_lcd();
}
 
void loop() {
  unsigned long time = millis();
  loop_input(time);
  loop_input_adc(time);
  loop_lcd(time);
  loop_serial();
}


note: remove spaces from include statements after copy from forum.
note2: Also added project as attachment because I get the above code not correct copy pasted.

Changed a lot;
- serial speed 115200
- config can be saved in eeprom
- soft startup via extra off duty
- mappable analog input to variable with range
- 'WIP' code for pulse seqence with offset into next step
- simple lcd ui via 2 button:
  lcd_menu_off; push0=enter menu, push1=manual trigger
  lcd_menu_sel; push0=next items,  push1=change value
  lcd_menu_val; push0=+ , push1=- , both is *10

Now about 30 settings (and 15 in lcd)
Code: [Select]

root@pulsefire: info
sys_lcd_time==200
sys_menu_time==4000
sys_adc_time==170
sys_adc_map0==0 0 0 0
sys_adc_map1==1 0 0 0
sys_adc_map2==2 0 0 0
sys_adc_map3==3 0 0 0
sys_adc_map4==4 1 30 300
sys_adc_map5==5 12 0 15
sys_input_time==5
sys_input_delay==300
sys_start_secs==0
sys_start_duty==50
pulse_steps==5
pulse_duty==0
pulse_trigger==0
pulse_mode==4
pulse_init==1
pulse_seq_off==6
pulse_seq_type==0
pulse_seq_data==65535
pulse_seq_len==15
train_loop==213
train_inc==0
train_tcnt==65376
train_clock==1
step_loop==0
step_inc==0
step_tcnt==96
step_clock==1
pulse_data==1
train_loop_cnt==140
train_state==1
step_loop_cnt==0
step_state==0
calc_train_speed==100000
calc_train_loop==9
calc_train_freq==0
calc_step_speed==100000
calc_step_loop==0
calc_step_freq_on==4
calc_step_freq_off==0
info_up==409868
info_cpu==16000000
info==done
root@pulsefire:


The phase shifting is now almost in via the seqence and offset but not fully correct yet, next todo request/calc freq and validate input.
And wanna check 8 bit or more output mode.
Is 8 outputs enough for push / pull setups or run 2 pulse fires ?
yes, 8 is enough, really the 6 you have is enough? where you going to to put the other 2?

got it loaded but the d4 push button dont work?

did you change any of the I/O?

i was awaiting this update ! :) thank you and the attachment of the code was alot better idea!

here is the setup i have,

see attachment

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #33, on May 5th, 2011, 07:08 PM »
Quote from firepinto on May 5th, 2011, 06:54 PM
That looks sweet!  Did you make the acrylic  piece or can you buy those?  So far I've destroyed any acrylic I've tried to cut. lol
Thanks! I got some of those from my work. They were throwing them out!

There shoe stands...

Email my your adress and I'll send you some??? If you want it!

~Russ

firepinto

RE: EPG Testing sequential pulsar using the Arduino
« Reply #34, on May 5th, 2011, 07:21 PM »
I've been thinking about making a rotor/disk/ring type thing for pressing magnets into. I would like to have toothed gears cut into it.:s  I just need to learn how to cut it with out disasters lol.  Might have to get into the plastic resin thing like skycollection does so well with.  I always thought acrylic craftsmanship was impressive.  I'll keep the shoe stands in mind :)
Quote from Rwg42985 on May 5th, 2011, 07:08 PM
Thanks! I got some of those from my work. They were throwing them out!

There shoe stands...

Email my your adress and I'll send you some??? If you want it!

~Russ

Forum Administrator

RE: EPG Testing sequential pulsar using the Arduino
« Reply #35, on May 5th, 2011, 09:34 PM »Last edited on May 6th, 2011, 12:45 AM by admin
Russ,

LOVE the ignition style starter for the project   Fine addition! Make sure we get to see you 'fire it up' .  Good work guys!  Can't wait for the next vid buddy!

Matt

txqNL

RE: EPG Testing sequential pulsar using the Arduino
« Reply #36, on May 6th, 2011, 12:41 AM »
Quote from Rwg42985 on May 5th, 2011, 06:47 PM
yes, 8 is enough, really the 6 you have is enough? where you going to to put the other 2?
got it loaded but the d4 push button dont work?

did you change any of the I/O?

i was awaiting this update ! :) thank you and the attachment of the code was alot better idea!

here is the setup i have,
see attachment
It looks you have to reserve 2&3 see;

D0/D1  = Rx/Tx Serial console (is mapped to usb on Arduino)
D2     = Interupt hardware trigger for pulse train via hall sensor (pulse_trigger=2)
D3     = Input0 Push button to the ground for menu and add
D4     = Input1 Push button to the ground for trigger/select/subtract
D5     = Interups hardware for timer clock (pulse_clock=6or7)

For more outs was thinking for a output_mode which used 2 8 bit shifters for output and lcd so the analog in come free again.

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #37, on May 7th, 2011, 12:55 PM »
Quote
For more outs was thinking for a output_mode which used 2 8 bit shifters for output and lcd so the analog in come free again.
I would say make that a more advanced setup?

So there is a basic 6 pulse output code...

And a'n advanced setup with 6-8 or 10 outputs...

So anyone can just grab the arduino and run with it... Then if they need more out's they can build the advanced setup???

Yeah?

So depending on what the user is trying to do they can build the setup they need?

So get the basic 6 output controller fully functioning then move to the advanced setup?

Once we get it worked out I can place it on the projects page in a nice platform for all to see, it can be listed under( others projects)  or something???

Sound good?

Thanks! ~Russ
 

txqNL

RE: EPG Testing sequential pulsar using the Arduino
« Reply #38, on May 15th, 2011, 11:20 AM »
Version 0.5 is almost done now, hope i can release it before end of the week.
Here are already some changes made;
- Seqence mode offset looks working now
- Fixed custom init data reversal in reverse mode
- Added input validation for Serial and Lcd.
- Given LCD UI all config options to change.
- Added auto push mode which pushes config changes to serial
- Added output masking for disabling some outputs
- Spilt info into 3 commands info_conf,info_data and info_calc
- Changed adc mapping now all config variables can be mapped.
- Fixed flashmode output steps limit
- Change _inc to _delta and added _tcnt_deltas's so now we have
4 config values which puts the fire in the pulse.
- start added auto lpm messurement code for freq tuning feature.
- start working on req_train_freq command.

So now working in the code for freq and I want to see some values
behind the dot for example Hz: 123.123
(but without using floats but need to get my mind around it first)

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #39, on May 15th, 2011, 11:44 AM »
Quote from txqNL on May 15th, 2011, 11:20 AM
Version 0.5 is almost done now, hope i can release it before end of the week.
Here are already some changes made;
- Seqence mode offset looks working now
- Fixed custom init data reversal in reverse mode
- Added input validation for Serial and Lcd.
- Given LCD UI all config options to change.
- Added auto push mode which pushes config changes to serial
- Added output masking for disabling some outputs
- Spilt info into 3 commands info_conf,info_data and info_calc
- Changed adc mapping now all config variables can be mapped.
- Fixed flashmode output steps limit
- Change _inc to _delta and added _tcnt_deltas's so now we have
4 config values which puts the fire in the pulse.
- start added auto lpm messurement code for freq tuning feature.
- start working on req_train_freq command.

So now working in the code for freq and I want to see some values
behind the dot for example Hz: 123.123
(but without using floats but need to get my mind around it first)
Oooooo..... Nice!!! Cant wait!!!!! Lost of hard work I can see!! Sweet!!! thank you!!! ~Russ

Nate

RE: EPG Testing sequential pulsar using the Arduino
« Reply #40, on May 16th, 2011, 11:39 AM »
Wow this thread is awesome. That is some serious code posted, I'll have to really really check that out when I get some free time.

For simple things I like to use the Timer1 library. With timer one you can write a small sketch (like 2 lines of code) to set a frequency and duty cycle without using delays. I'm not sure how many pins can be used but its quick.

http://www.arduino.cc/playground/Code/Timer1
-Nate

txqNL

RE: EPG Testing sequential pulsar using the Arduino
« Reply #41, on May 21st, 2011, 04:26 PM »Last edited on May 21st, 2011, 04:28 PM by txqNL
Quote from txqNL on May 15th, 2011, 11:20 AM
Version 0.5 is almost done now, hope i can release it before end of the week.
Here are already some changes made;
- Seqence mode offset looks working now
- Fixed custom init data reversal in reverse mode
- Added input validation for Serial and Lcd.
- Given LCD UI all config options to change.
- Added auto push mode which pushes config changes to serial
- Added output masking for disabling some outputs
- Spilt info into 3 commands info_conf,info_data and info_calc
- Changed adc mapping now all config variables can be mapped.
- Fixed flashmode output steps limit
- Change _inc to _delta and added _tcnt_deltas's so now we have
4 config values which puts the fire in the pulse.
- start added auto lpm messurement code for freq tuning feature.
- start working on req_train_freq command.

So now working in the code for freq and I want to see some values
behind the dot for example Hz: 123.1
(but without using floats but need to get my mind around it first)
The freq code works!!, settled for one decimal behind the dot to
have it work best for large range selection.
Lets do an example;
Code: [Select]

defaults - Lets start with defaults ~ 2 hz train step
pulse_mode 4 - Seqence mode
pulse_seq_data 65535 - Set output data to full on so it looks like mode 1 but /16 slower
pulse_seq_off 15 - Set (currently) max offset in future step of output
pulse_seq_type 16 - Select internal seq_data where 16 is lawton like
sys_push_mode 1 - Enable push conf changed to serial
sys_adc_map 4 102 1 1250 - Map analog4 to freq request
(Turn pot to have effect because of sys_adc_jitter removal)
pulse_mask 65530 - Bitfield so disabled output 0 and 2. (65535-1-4)
pulse_mask 65535 - enable all outputs again
sys_adc_map 5 11 0 100 - Map analog4 to pulse duty
info_conf - Show all config values
save - Save all config value


There is limited untested support for 4 row lcd set the #define LCD_SIZE_ROW to 4 to use it.
To get correct hz on lcd display use; pulse_steps 2, pulse_mode 2 and pulse_duty 0 and then "req_train_freq 103 9" to selects 103.9 hz which calculated to 51.9hz and is messured to 51hz on output 0.

Know errata:
- pulse_steps 1 does not pulse
- lpm code is yet connected
- _delta seems not be working in pulse_mode 4
- req_* requests are wip
- Higher speeds breaks lcd/serial refresh (2out~600hz,6out~250hz)
- In higher freq don't use req_train_freq but calc yourself for best result
- bugs,etc

While working on the freq selection code I noticed there are also some
nice compare registers in main train timer so I'm thinking of changing
the code for step stuff (which is ~stepduty) to this timer so that timer2 comes free again which may come in handy for a PLL function.

Or maybe change that to something diffent for getting higher speeds.
Because on higher speed resolution goes down and lcd/serial interface start to response to slow.



~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #42, on May 25th, 2011, 08:00 AM »
Willaim! Sweet! Just got back to the lab... Amost ready to pulse the main cores! Will be playing with this this weekend! Then start testing next week there will be lots ahead for my research! Next 2 weeks are all mine! :)

I'll post update video soon!

Thank you sir!!!!!

~Russ

~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #43, on May 26th, 2011, 02:53 AM »
Quote from Rwg42985 on May 25th, 2011, 08:00 AM
Willaim! Sweet! Just got back to the lab... Amost ready to pulse the main cores! Will be playing with this this weekend! Then start testing next week there will be lots ahead for my research! Next 2 weeks are all mine! :)

I'll post update video soon!

Thank you sir!!!!!

~Russ
its done! (for the most part) ill post a video soon!

see attached photos!


txqNL

RE: EPG Testing sequential pulsar using the Arduino
« Reply #44, on May 26th, 2011, 06:21 AM »
Quote from Rwg42985 on May 26th, 2011, 02:53 AM
its done! (for the most part) ill post a video soon!
see attached photos!
Thats a very heavy build you have there :)
Good to see the opto isolaters, I now need to get looking
for new cpu and/or Arduino board just killed my output pin 3 & 4 :(
while playing with solid state coil shorting.
Just before I wanted to start change some code so the pulse_seq_data can be set for every output which makes more custom pulses possible.





~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #45, on May 26th, 2011, 07:46 AM »
Quote from txqNL on May 26th, 2011, 06:21 AM
Quote from Rwg42985 on May 26th, 2011, 02:53 AM
its done! (for the most part) ill post a video soon!
see attached photos!
Thats a very heavy build you have there :)
Good to see the opto isolaters, I now need to get looking
for new cpu and/or Arduino board just killed my output pin 3 & 4 :(
while playing with solid state coil shorting.
Just before I wanted to start change some code so the pulse_seq_data can be set for every output which makes more custom pulses possible.
!!!!!! O!!!! Yes! You better get one fast! :) your the mastermind behind the code! Can't do with out ya!!!

Also, I'll be posting the schmatic soon!

Thanks man!!! Now get some ordering in order! :)

Ps. Coil shorting is vary interesting!  :)

~Russ


~Russ

RE: EPG Testing sequential pulsar using the Arduino
« Reply #47, on May 26th, 2011, 01:13 PM »
Isolator Driver Schematic!

This is just the PCB and schematic, I will draw up the Arduino Schematic soon!

[attachment=73]
[attachment=74]
[attachment=75]

Let me know what you think! ~Russ

firepinto

RE: EPG Testing sequential pulsar using the Arduino
« Reply #48, on May 26th, 2011, 01:17 PM »
That's sweet!

Twin turbo even :D  I cant wait to see some magno-gas flowing through the PEX.  

I ordered myself an Arduino Uno, it should be at my door step when I get home Fri.  Is that compatible with the code your using?  I think I may even have an LCD for it that I can borrow off my security panel.:cool:

Nate
Quote from Rwg42985 on May 26th, 2011, 02:53 AM
Quote from Rwg42985 on May 25th, 2011, 08:00 AM
Willaim! Sweet! Just got back to the lab... Amost ready to pulse the main cores! Will be playing with this this weekend! Then start testing next week there will be lots ahead for my research! Next 2 weeks are all mine! :)

I'll post update video soon!

Thank you sir!!!!!

~Russ
its done! (for the most part) ill post a video soon!

see attached photos!