Record and play function using shift registers.

Hi everyone.

I've been searching the web for a way to sketch a record and play function in conjunction with a sketch which controls 74HC165 shift registers (for push-button inputs) and 74HC595 shift registers (for LED outputs). The record and play functions should record sequence, timing and must be able to record 2 buttons which are being pushed at a time independently of each other (meaning a different timer for each button) at a time (just like how the sketch allows for 2+ buttons to be pushed and 2+ LEDs to be turned on "simultaneously").

I'm not looking for your particular method on how to do this. I'm more-so looking for sources from which I may learn to program this myself, however, your methods would be appreciated. But, please: If you do decide to provide a method, note that I do not know much about the Arduino or programming, and please be ready to answer questions, however stupid you may assume them to be. If you do not agree with having to answer questions, don't even bother providing a method.

This is the schematic for my circuit which uses one parallel IN/serial OUT shift register to receive inputs from pushbuttons, and another serial IN/parallel OUT shift register to send outputs to LEDs:

|500x375

This is the code which controls the shift registers (both in link and code format):

http://pastebin.com/ixaQ26Y1

      //Pin connected to latch pin (ST_CP) of 74HC595 PIN 12
      int latchPin = 10;
      //Pin connected to clock pin (SH_CP) of 74HC595 PIN 11
      int clockPin = 11;
      ///Pin connected to Data in (DS) of 74HC595    PIN 14
      int dataPin = 12;

      int load =           7; //connects load to pin 1          74hc165
      int clockEnablePin = 4; //connects to clock enable pin 15 74hc165
      int dataIn =         5; //connects to the data pin 7      74hc165
      int clockIn =        6; //connects to the clock pin 2     74hc165
      
    
    void setup()   {                

          Serial.begin(9600);
          
          //74HC165
          pinMode(load, OUTPUT);        // pin 1 connect to pin 7 on arduino
          pinMode(dataIn, INPUT);       // pin 7 connect to pin 5 on arduino
          pinMode(clockIn, OUTPUT);     // pin 2 connect to pin 6 on arduino
          pinMode(clockEnablePin, OUTPUT); // pin 15 connect to pin 4 on arduino
          

          //74HC595
          pinMode(latchPin, OUTPUT);  // pin 12 connect to pin 10 on arduino
          pinMode(dataPin, OUTPUT);   // pin 14 connect to pin 12 on arduino
          pinMode(clockPin, OUTPUT);  // pin 11 connect to pin 11 on arduino

         }


    void loop() {

          digitalWrite(load,LOW);
          delayMicroseconds (10);
          digitalWrite(load,HIGH);
          delayMicroseconds (10);

          digitalWrite(clockIn, HIGH);
          digitalWrite(clockEnablePin, LOW);
          byte incoming = shiftIn(dataIn, clockIn, LSBFIRST);
          digitalWrite(clockEnablePin,HIGH);

            digitalWrite(latchPin, LOW);
            shiftOut(dataPin, clockPin, LSBFIRST, incoming); //1-8
            digitalWrite(latchPin, HIGH);
            delay(10);

          Serial.print("Pin States:\r\n");
          Serial.println(incoming, BIN);
          delay(10);
    }

Trolls, flamers and assholes please continue to the next topic.

Thank you for your time and patience to those who qualify.

Edit: The playback function should loop the recording until the play button is pushed again. Record should be a push button as well.

Edit 2:

In order to facilitate comprehension of how exactly this program should operate, I've written a short guide which specifically explains the functions of each component.

I hope I was clear!

[u]HOW RECORD/PLAY SHOULD OPERATE[/u]

There are 8 buttons used as inputs (using 74HC165 Shift Register).

There are 8 LEDs receiving outputs (using 74HC595 Shift Register).

6 Buttons will control 6 LEDs, to be labeled Action Buttons & Action LEDs.

2 Buttons will control 2 LEDs, to be labeled {Record Button/LED} & {Play Button/LED}.

While record button and play buttons are LOW, the device will function normally, turning Action LEDs on and off when Action push buttons are pressed without recording time between Action button presses.

If the record button is pushed, the Action buttons will still function normally (turning Action LEDs on and off) but a timer will begin to countdown (as soon as record button bit is HIGH) (If Record button is held down, will not affect the fact that timer is now active) and Record LED bit will go HIGH and remain HIGH until record button is pushed once more (which would signify stop recording).

Then, if the first(nth) Action button is pushed, the record program will save the time the nth Action button was pushed (meaning the time the nth bit in the registers became HIGH) as well as the time the Action button was released (the time the nth bit went LOW), and continue listening for the next time an Action button is pushed.

If an Action button is pushed and held down, the program will note that one Action button is HIGH, and note how long it is HIGH for until it is released, while listening for another action button press. If another Action button is pushed, the program will note the place in time the overlapping action button was pushed. When either Action button is released, the program will note the time that button was released (whether it was the first button which was held down for xtime, or the second button which was pushed while 1st (held) button was being clocked).

When finished recording: the timer will continue to record until the record button is pushed and it goes LOW, also turning the record LED LOW. If the record button is held down when it’s meant to enter a LOW state, it will still signify a LOW as soon as it is pushed, also turning the record LED LOW.

Once ready for playback: the playback button will go HIGH when pushed OR held, playing back the recorded actions in a loop until the playback button is pushed or held again (meaning stop).

Trolls, flamers and assholes please continue to the next topic.

Then, I guess I will, too.

Is that putting people off from responding? If you know you're here just to degrade people's ideas and questions, then good. I'd prefer you just moved on. Thank you.

ok.

Reading and writing to the shift registers is easy. Have an array of byte[8] for each, and write functions to read all the pins off the PI/SO register to it's array and another function to write all the values of the other array to the SI/PO register. if you read at the top and write at the bottom of your loop, then what's hapenning in the real world and what's hapenning in your processor will match.

Oh, you can do it by bit-fiddling too, but just using a pair of array is way easier.

So then your question becomes: each time through the loop, given the values in the input, the current time, and any state the sketch has stored - what should be done with the output?

Not counting the button timeouts, your sketch can be in four states: normal, counting down to record, recording, or playing. How it moves between those states (in the case of the record button) depends on the buttons, and by that I mean there's a layer of abstraction between the pin and the button, because your 'record' button is a complicated object in its own right.

Frankly, I'd start wit the record button. Write a function to manage the record button state. Actually, a pair of functions: 'hasTheRecordButtonBeenPressed' (which manages your click-and-hold timing) and 'hasTheRecordButtonBeenReleased'. This pair of functions has a set of variables that they use - I would be inclined to stuff it into a class.

Once you have your functions for the play and record buttons, then it's time to look at the state machine for the sketch as a whole:

loop() {
  read the button register;
  call the function to manage the play buttonn;
  call the function to manage the record button;


  switch(currentState) {
  case IDLE:
    if(play button pressed) {
      initialize playback;
      currentState = PLAY;
    }
    else if(record button pressed) {
      initialize record;
      currentState = RECORD;
    }
    break;

    case PLAY:
      if(we are done playing) {
        initialize IDLE mode;
        currentState = IDLE;
      }
      braeak;
  

   case RECORD:
     if(record button pressed, or we have reaced the limit of how many notes we can record) {
        initialize IDLE mode;
        currentState = IDLE;
     }
     break;

  }

  switch(currentState) {
  case IDLE:
    echo the input action buttons to the ouput LEDs.
    break;

    case PLAY:
      if it's time to change the output leds according to our recording
        but the next note in the recording to the utput LEDs
      break;
  
   case RECORD:
     if any of the action buttons has changed since last time we looked at them
       record the new action state, the time, and increment 'number of notes'
     break;

  }

  write the output array to the LED shift register;

}

After than, you have the issue of how you are going to record these sequences of notes. The best way is bit-fiddling, but this is much simpler to program, even if it is wasteful of space:

const int MAX_ACTIONS_IN_RECORDING = 20;

struct recording {
  byte[6] actions;
  unsigned long durationMs; (or 'time since start of playback ms')
}
the_recording[MAX_ACTIONS_IN_RECORDING];

int numOfActionsInRecording = 0; // nothing recorded so far

Look up the memcpy function in the libc library to read about how to move the arrays of byte around. After you have it working, then you can tighten up how everything stores its data.

And - that should do the job. Of course, this is only a guideline for how to go about it..

Wow, I don't think I could have asked for anything more.

Thank you so much PaulMurrayCBR!

This is gonna work!

No doubt I'll run into some hiccups. Ill do my best to solve any problems I run into on my own; but if I can't, I hope folks don't mind if I come back around!

Thanks again! Thank you. :) :)

Rixius44: Wow, I don't think I could have asked for anything more.

Thank you so much PaulMurrayCBR!

This is gonna work!

Arrgh! I forgot to include a state for"flashing the "ready to record" led, but not actually recording yet". Unless that flashing bizzo is part of the record button functionality: it happens during the hold-down.

But you get the gist. Many, many arduino sketches follow this "state machine" form.

I like to have my states in an enumerated type, and I have a rule that all state transitions (assignments to the state variable) get done only in the main switch() statement, so there's no hidden gotchas elsewhere in the code.

I also like to put complex substate things (eg: record buttons with a delay) into classes.

but the basic form is the same.

stuff I have remembered;

loop() {
  put stuff I have remembered into local variables;
  read inputs into stuff I have remembered;

  // first, state transitions

  switch(state) {
  case state_1:
    if(I need to move to state 2) {
      do stuff to cancel state 1;
      do stuff to move into state 2;
      remember the fact that I changed state at the current millis();
      state = state_2;
    } 
    else if(I need to move to state 3) {
      do stuff to cancel state 1;
      do stuff to move into state 3;
      remember the fact that I changed state at the current millis();
      state = state_3;
    } 
    // etc
    break;

  case state_2:
    // same as state 1
  }

  // next, ongoing time slaices for aout current state

  switch(state) {
  case state_1:
    do ongoing stuff for state 1, eg flashing lights, move actuators, maybe remember important things
    // this often involves looking at the time that we moved into this state
    break;
  
  case state_2:
    // same as state 1
  }

}

Understood. Working on figuring things out at the moment. Thanks for the extra information! I'll implement it.

Hi there Rixius,

I am working on pretty much the exact project you seem to have been, apart from rather than LEDs I need relays to switch on and off. If you got the program working would you be willing to share the final code? It would obviously save me weeks of work as I am very new to arduino.

Many thanks

Sam.