EtchAsketch code modification

edited February 27 in Arduino

I am working on a science project with my son that involves generating a force as a function of time plot. My idea is to modify the LittleBits EtchAsketch code for this purpose. Accordingly, I would like to know how I could modify the EtchAsketch processing code to measure time along the x-axis. Moreover, I would like to show the numerical values on the vertical and horizontal axes and keep the measured signal. The LittleBits Arduino is based on the Lenoardo board. Any help is welcome. Thanks!

Answers

  • Can you provide a link to the code? It will be great if you comment about your experience with processing (or programming) so people can address your question accordingly.

    Kf

  • Sorry, the link can to both the processing code and arduino code can be found here: http://littlebits.cc/projects/diy-etch-a-sketch . I am a beginner in terms of writing code. I have only made slight modifications to code, but I have never written code from scratch.

  • Answer ✓

    Hi @trinity

    There is a good example that you can use for drawing your input data. In the Processing IDE, go to menu File>>Examples and then click on Basics>>Inputs>>MouseSignals and you will see a cool sketch of plotting mouse signals over the width of the screen. The width of the screen projects time in this example. Now, this example will not work right away as you will need to modify it to suit it to your needs. In the code below I have attempted to write snippet that you could use with your Arduino. However, please keep in mind I didn't have the chance to test it on an actual device. I generated my own data for debugging purposes. Check it out and let me know if you have any questions or issues there. Notice this example is heavily modified from the website you provided. I manage serial incoming data using provided functions from the Serial library. You should be able set any screen size at the beginning of your sketch and the code should work.

    Kf

        //REFERENCE: https://forum.processing.org/two/discussion/20685/etchasketch-code-modification#latest
    
    import processing.serial.*;
    
    final int kMAX_DATA_VAL=512; //9bit resolution
    final int kVERTICAL_MARGIN=int(kMAX_DATA_VAL*0.1);
    
    // Serial Port variables
    Serial myPort;
    
    int bSize;  //buffer size
    int index =-1;
    
    String globalBuffer;
    int[] valuesx;
    int[] valuesy;
    
    
    void settings() {
      size(512, 512);
    }
    
    void setup()
    {
      bSize=width;
      valuesx = new int[bSize];
      valuesy = new int[bSize];
    
      noSmooth();
      println(Serial.list()); // use this to determine which serial port is your littleBits Arduino
      // EXAMPLE: myPort = new Serial(this, Serial.list()[ your serial port number ], 9600);
      myPort = new Serial(this, Serial.list()[0], 9600);
      myPort.bufferUntil('\n');
    
      background(87, 36, 124);
      strokeWeight(5);
    }
    
    
    void draw() {
    
      ////ENABLE NEXT LINE WHEN NO SERIAL EVENTS AVAILABLE
      //loadArtificialData();
    
      surface.setTitle("Index="+index+" time="+millis()/1000+" sec");
      boolean drawNow=processInputData(globalBuffer);
    
      if (drawNow==true) {
        //Reset background for new data wave
        if ((index)<=1) {
          background(87, 36, 124);
          return;
        }
    
        drawLine(index, valuesx, -width, width, color(255, 25, 25));    
        drawLine(index, valuesy, -kMAX_DATA_VAL/2, kMAX_DATA_VAL/2, color(250, 205, 25));
      }
    
    }
    
    
    void serialEvent(int serial)
    {
      globalBuffer=myPort.readStringUntil('\n');
    }
    
    //Returns true when valid data is available
    boolean processInputData(String buff) {
    
      if (buff==null)
        return false;
    
      float[] mbuff=float(split(buff, ","));
    
      //Only process data with two tokens, discard anything else
      if (mbuff.length!=2)
        return false;
    
      index++;
      if (index==bSize)
        index=0;
    
      valuesx[index] = int(mbuff[0]);
      valuesy[index] = int(mbuff[1]); 
    
      buff=null;  //Reset buffer for next data stream (IMPORTANT!!!)
      return true;
    }
    
    //Draws a line from previous index to current index
    void drawLine(int idx, int[] data, float LRange, float HRange, color cc) {
      float y1=map(data[idx-1], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      float y2=map(data[idx], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      stroke(cc);
      line(idx-1, y1, idx, y2);
    }
    
    //This function is for testing/debugging purposes
    //Loads data generated from a sine wave
    void loadArtificialData() {
      globalBuffer=str(index)+",";
      globalBuffer+=str(kMAX_DATA_VAL/2*sin(radians(index%width)*2*3.1415/0.8));
    
      println(globalBuffer);
    }
    
  • edited February 10

    Thanks, I will try it out and let you know if it works!

  • The code seems to measure time by the second, but it is not plotted with what we measure on the y-axis. In fact, there are no values plotted on the y-axis. It would also be convenient to record the time with 10 ms resolution. Do you have any suggestions?

  • Answer ✓

    code seems to measure time by the second, but it is not plotted with what we measure on the y-axis

    You need to remember I do not have a system with me so you need to be more specific of what you need. As far as I know, you are reading two sensors, from what I saw in the original pde file. The code above takes those two sensors' inputs and plot then over time. However, time here is not time as in seconds, but relative time as data arrives and gets processed by Processing.

    There are three ways to interpret time:

    float currentTime=index*frameRate(); This gives time as measured from the left side of the screen, the left side being 'zero' seconds. This method is not totally accurate but enough for your application. Notice this time is reset every time the data reaches the right end of the sketch.

    float currentTime=frameCount*frameRate(); Time relative to the beginning of the program. Not accurate but enough for demonstrations.

    Another way to get relative time is using int currentTime=millis() right when the lines are drawn. This time is in milliseconds and is relative to the start of the program.

    For line 55 and 56, you need to change the LRange and HRange values in that function. You choose the values based on the range of your input data. If it is an accelerometer with 10 bit resolution, it means it can output data from 0 to 1023 (total number of entries is 1024 or 2^10 where 10 is related to 10 bits). However I don't know what sensor you are using and what is their output range. This information is provided in your sensor's data sheet (or specification sheet).

    To see if it is working, or if you are receiving data, introduce this next line in the previous code at line 54: println(millis()+"Processing idx="+index+" "+valuesx[index]+" "+valuesy[index]); Let your sketch run for about 5 seconds. Copy and paste here what you get from the console. To do that, slect the text in the console and hit ctrl+c to copy. You can paste it here in the forum with ctrl+v.

    Kf

  • After adding the println in the last posted code, you don't get any other data, just COM3?

    Kf

  • Correct.

  • Although it was able to run both times (before and after adding line 54) there were some errors. Perhaps, if I correct the two errors, I will see more.

  • What do the errors say? Please copy and paste those errors here.

    Kf

  • Type String[] of the last argument to method println(Object...) doesn't exactly match the vararg parameter type. Cast to Object[] to confirm the non-varargs invocation, or pass individual arguments of type Object for a varargs invocation. line 30 The value of the parameter serial is not used line 62

  • Please. paste your code here.

    Kf

  • I did not modify your code. I just copy-pasted it and ran it.

  • edited February 12 Answer ✓

    Make sure this line is enable in line 54: println(millis()+"Processing idx="+index+" "+valuesx[index]+" "+valuesy[index]);

    You should see lots of lines being print out in the console.

    Although it was able to run both times (before and after adding line 54) there were some errors. Perhaps, if I correct the two errors, I will see more.

    Change the function in line 62 for this

    void serialEvent (Serial port) {   
      globalBuffer=myPort.readStringUntil('\n');
    }
    

    Kf

  • It seems to work now as intended; however, perhaps I did not make everything clear. We only need one input, not the two from the little bits sketch. Nevertheless, it is collecting data. I was wandering if it is possible the have the start recording once an in input voltage is detected and stop when it is not? This is the time that we need, as well as the maximum detected voltage. Thanks again for all of your help!!!

  • Answer ✓

    That will required few more lines of code and to know exactly what sensor you are using on your arduino unit. After you collect the data, you can use a function like max() on your stored values. To trigger data collection, you need to know the output range of your sensor in order to define when an input voltage is detected.

    Kf

  • I am currently using a1, perhaps it is value x. However, I could switch to either terminal a1 or a0. The values range from 0 to 5 volts.

  • @trinity

    Do you have an Arduino UNO or the same as in the link, a Leonardo version?

    Kf

  • It is a Leonardo board.

  • edited February 16 Answer ✓

    Check this code. You need to adjust the value of swTRIGGER which is the software trigger that enables data collection. Right now I set the value to 25% of the maximum input (25% of 10 bits, in other words, 0.25*1024=256 which is 25% * 5V= 1.25V

    When the input of interest goes below the threshold, data collection stops, the data is saved in a file that you enter and the program will exit.

    Data stored is in 3 columns following the the format "time,dataX,dataY", where the time is in seconds.

    Please keep in mind I didn't run the code with an Arduino unit. However it was tested with the artificial data generation function included.

    Kf

    //REFERENCE: https://forum.processing.org/two/discussion/20685/etchasketch-code-modification#latest
    //Leonardo specs: https://www.arduino.cc/en/Main/ArduinoBoardLeonardo
    //  Analog Inputs: A0-A5, A6 - A11 (on digital pins 4, 6, 8, 9, 10, and 12). The 
    //  Leonardo has 12 analog inputs, labeled A0 through A11, all of which can also 
    //  be used as digital i/o. Pins A0-A5 appear in the same locations as on the Uno; 
    //  inputs A6-A11 are on digital i/o pins 4, 6, 8, 9, 10, and 12 respectively. Each 
    //  analog input provide 10 bits of resolution (i.e. 1024 different values). By default 
    //  the analog inputs measure from ground to 5 volts, though is it possible to change 
    //  the upper end of their range using the AREF pin and the analogReference() function.
    //REFERENCE: https://forum.processing.org/two/discussion/35/how-to-get-the-filepath-from-selectinput
    //REFERENCE: https://forum.processing.org/two/discussion/14595/simple-file-operations/p1
    
    
    import processing.serial.*;
    
    final int kMAX_DATA_VAL=1024; //10bit resolution
    final int kVERTICAL_MARGIN=int(kMAX_DATA_VAL*0.1);
    
    final int swTRIGGER = int(0.25*kMAX_DATA_VAL);  //Software trigger: 25% of input range
    
    IntList dataTime;  //Stores all data to be saved on file
    IntList datax;
    IntList datay;
    
    // Serial Port variables
    Serial myPort;
    
    int bSize;  //buffer size
    int index =-1;
    boolean collectDataNow=false;
    
    String globalBuffer;
    int[] valuesx;   //Data been displayed on screen
    int[] valuesy;
    
    
    void settings() {
      size(512, 512);
    }
    
    void setup()
    {
      bSize=width;
      valuesx = new int[bSize];
      valuesy = new int[bSize];
    
      noSmooth();
      println(Serial.list()); // use this to determine which serial port is your littleBits Arduino
      // EXAMPLE: myPort = new Serial(this, Serial.list()[ your serial port number ], 9600);
      myPort = new Serial(this, Serial.list()[0], 9600);
      myPort.bufferUntil('\n');
    
      dataTime=new IntList();
      datax=new IntList();
      datay=new IntList();
    
      background(87, 36, 124);
      strokeWeight(5);
    }
    
    
    void draw() {
    
      ////ENABLE NEXT LINE WHEN NO SERIAL EVENTS AVAILABLE
      //loadArtificialData();
    
      surface.setTitle("Index="+index+" time="+millis()/1000+" sec");
      boolean drawNow=processInputData(globalBuffer);
    
      dataCollectionManager();
    
      if (drawNow==true) {
        //Reset background for new data wave
        if ((index)<=1) {
          background(87, 36, 124);
          return;
        }
    
        drawLine(index, valuesx, 0, kMAX_DATA_VAL, color(255, 25, 25));   
        drawLine(index, valuesy, 0, kMAX_DATA_VAL, color(250, 205, 25));
      }
    }
    
    void dataCollectionManager() {
    
      if (valuesx[index] >swTRIGGER || valuesy[index] >swTRIGGER) {
        collectDataNow=true;
        println("Data collection started at "+millis()/1000+" seconds");
        println("Last valueX is " + valuesx[index] + " Over threshold?:" + (valuesx[index] >swTRIGGER) );
        println("Last valueY is " + valuesy[index] + " Over threshold?:" + (valuesy[index] >swTRIGGER) );
        println("Current threshold is " + swTRIGGER +" or" + nfp((5.0*swTRIGGER)/kMAX_DATA_VAL, 1, 3) + " volts");
      }
    
      //Store data only if incoming data is over threshold
      if (collectDataNow==true) {
        dataTime.append(millis());
        datax.append(valuesx[index]);
        datay.append(valuesy[index]);
      }
    
      //Stop data collection and exit only :
      // [1] After data collection was enabled and
      // [2] After input signal goes under threshold value
      if (collectDataNow==true) {
        if (!(valuesx[index] <swTRIGGER || valuesy[index] <swTRIGGER)) {
          noLoop();
          println("Data collection stopped at "+millis()/1000+" seconds");
          selectOutput("Save data as...", "saveData", dataFile(""));
        }
      }
    }
    
    void saveData(File selection) {
      PrintWriter output;
      if (selection == null) {
        println("Window was closed or the user hit cancel. Data was NOT saved!");
      } else {    // Error handling in case of bad filenames etc. try ... catch ???
        try {
          String filename = selection.getAbsolutePath();
          output = createWriter(filename);      
    
          //Header of File
          output.println("Time[secs],dataX,dataY");
    
          for (int i = 0; i < datax.size(); i++)
            output.println(dataTime.get(i)+","+datax.get(i)+","+datay.get(i)); // Write each patch as a line of comma separated values
    
          output.flush();
          output.close();
          println("Data saved at "+filename+". Exiting...\n\n\nNormal termination.");
          exit();
        }
        catch (Exception e) {
          println("Error while saving... " + e);
        }
      }
    }
    
    void serialEvent (Serial port)  
    {
      globalBuffer=myPort.readStringUntil('\n');
    }
    
    //Returns true when valid data is available
    boolean processInputData(String buff) {
    
      if (buff==null)
        return false;
    
      float[] mbuff=float(split(buff, ","));
    
      //Only process data with two tokens, discard anything else
      if (mbuff.length!=2)
        return false;
    
      index++;
      if (index==bSize)
        index=0;
    
      valuesx[index] = int(mbuff[0]);
      valuesy[index] = int(mbuff[1]);
    
      buff=null;  //Reset buffer for next data stream (IMPORTANT!!!)
      return true;
    }
    
    //Draws a line from previous index to current index
    void drawLine(int idx, int[] data, float LRange, float HRange, color cc) {
      float y1=map(data[idx-1], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      float y2=map(data[idx], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      stroke(cc);
      line(idx-1, y1, idx, y2);
    }
    
    //This function is for testing/debugging purposes
    //Loads data generated from a sine wave
    void loadArtificialData() {
      globalBuffer=str(index)+",";
      globalBuffer+=str(kMAX_DATA_VAL/2*sin(radians(index%width)*2*3.1415/0.8));
    
      println(globalBuffer);
    }
    

    ************EDITED line 149

  • line 86 gave me the following error message: ArrayIndexOutofBoundsException:-1

  • Answer ✓

    @trinity

    Please move line 70 to after line 72.

    Kf

  • I will make this change, but I forgot to mention that there was a similar message as before:

    Type String[] of the last argument to method println(Object...) doesn't exactly match the vararg parameter type. Cast to Object[] to confirm the non-varargs invocation, or pass individual arguments of type Object for a varargs invocation.

    The value of the parameter serial is not used

  • Answer ✓

    Line 149 should be:

    void serialEvent (Serial port) {  
      globalBuffer=myPort.readStringUntil('\n');
    }
    

    Is that where the error points to?

    Kf

  • No, the error was at lines 48 and 139.

  • The line 149 addition has some errors, but it will run with the previous correction. However, it does not begin with the threshold voltage; but rather, it is continuously running as before.

  • Answer ✓

    Sorry, I meant to say line 139, not 149. So now it is working?

    Kf

  • Yes, it seems to be working, but it is giving the start time and not the duration of time, i.e. start input to end input.

  • Line 149 above is an empty line, so I am not sure what line you are referring to. Notice that warnings are not a problem when running any code. Please copy and paste your error message here. In regards to the program running continuously, it is because the input data is always above threshold and never goes under threshold. I suggest you tweak the threshold percentage from 10% to 90%. That is, in line 19 in the previous post with code, instead of 0.25, try values between 0.10 to 0.90.

    If you have any problems, send me a private message with your latest code. Please format the code before you send it.

    Kf

  • When you open the file, duration is calculated by taking the last time and subtracting the first time.

    Kf

  • edited February 17
    //REFERENCE: <a href="https://forum.processing.org/two/discussion/20685/etchasketch-code-modification#latest" target="_blank" rel="nofollow">https://forum.processing.org/two/discussion/20685/etchasketch-code-modification#latest</a>;
    //Leonardo specs: <a href="https://www.arduino.cc/en/Main/ArduinoBoardLeonardo" target="_blank" rel="nofollow">https://www.arduino.cc/en/Main/ArduinoBoardLeonardo</a>;
    //  Analog Inputs: A0-A5, A6 - A11 (on digital pins 4, 6, 8, 9, 10, and 12). The 
    //  Leonardo has 12 analog inputs, labeled A0 through A11, all of which can also 
    //  be used as digital i/o. Pins A0-A5 appear in the same locations as on the Uno; 
    //  inputs A6-A11 are on digital i/o pins 4, 6, 8, 9, 10, and 12 respectively. Each 
    //  analog input provide 10 bits of resolution (i.e. 1024 different values). By default 
    //  the analog inputs measure from ground to 5 volts, though is it possible to change 
    //  the upper end of their range using the AREF pin and the analogReference() function.
    //REFERENCE: <a href="https://forum.processing.org/two/discussion/35/how-to-get-the-filepath-from-selectinput" target="_blank" rel="nofollow">https://forum.processing.org/two/discussion/35/how-to-get-the-filepath-from-selectinput</a>;
    //REFERENCE: <a href="https://forum.processing.org/two/discussion/14595/simple-file-operations/p1" target="_blank" rel="nofollow">https://forum.processing.org/two/discussion/14595/simple-file-operations/p1</a>;
    
    
    import processing.serial.*;
    
    final int kMAX_DATA_VAL=1024; //10bit resolution
    final int kVERTICAL_MARGIN=int(kMAX_DATA_VAL*0.1);
    
    final int swTRIGGER = int(0.25*kMAX_DATA_VAL);  //Software trigger: 25% of input range
    
    IntList dataTime;  //Stores all data to be saved on file
    IntList datax;
    IntList datay;
    
    // Serial Port variables
    Serial myPort;
    
    int bSize;  //buffer size
    int index =-1;
    boolean collectDataNow=false;
    
    String globalBuffer;
    int[] valuesx;   //Data been displayed on screen
    int[] valuesy;
    
    
    void settings() {
      size(512, 512);
    }
    
    void setup()
    {
      bSize=width;
      valuesx = new int[bSize];
      valuesy = new int[bSize];
    
      noSmooth();
      println(Serial.list()); // use this to determine which serial port is your littleBits Arduino
      // EXAMPLE: myPort = new Serial(this, Serial.list()[ your serial port number ], 9600);
      myPort = new Serial(this, Serial.list()[0], 9600);
      myPort.bufferUntil('\n');
    
      dataTime=new IntList();
      datax=new IntList();
      datay=new IntList();
    
      background(87, 36, 124);
      strokeWeight(5);
    }
    
    
    void draw() {
    
      ////ENABLE NEXT LINE WHEN NO SERIAL EVENTS AVAILABLE
      //loadArtificialData();
    
      surface.setTitle("Index="+index+" time="+millis()/1000+" sec");
      boolean drawNow=processInputData(globalBuffer);
    
    
    
      if (drawNow==true) {
        //Reset background for new data wave
        if ((index)<=1) {
          background(87, 36, 124);
          return;
        }
     dataCollectionManager();
        drawLine(index, valuesx, 0, kMAX_DATA_VAL, color(255, 25, 25));   
        drawLine(index, valuesy, 0, kMAX_DATA_VAL, color(250, 205, 25));
      }
    }
    
    void dataCollectionManager() {
    
      if (valuesx[index] >swTRIGGER || valuesy[index] >swTRIGGER) {
        collectDataNow=true;
        println("Data collection started at "+millis()/1000+" seconds");
        println("Last valueX is " + valuesx[index] + " Over threshold?:" + (valuesx[index] >swTRIGGER) );
        println("Last valueY is " + valuesy[index] + " Over threshold?:" + (valuesy[index] >swTRIGGER) );
        println("Current threshold is " + swTRIGGER +" or" + nfp((5.0*swTRIGGER)/kMAX_DATA_VAL, 1, 3) + " volts");
      }
    
      //Store data only if incoming data is over threshold
      if (collectDataNow==true) {
        dataTime.append(millis());
        datax.append(valuesx[index]);
        datay.append(valuesy[index]);
      }
    
      //Stop data collection and exit only :
      // [1] After data collection was enabled and
      // [2] After input signal goes under threshold value
      if (collectDataNow==true) {
        if (!(valuesx[index] <swTRIGGER || valuesy[index] <swTRIGGER)) {
          noLoop();
          println("Data collection stopped at "+millis()/1000+" seconds");
          selectOutput("Save data as...", "saveData", dataFile(""));
        }
      }
    }
    
    void saveData(File selection) {
      PrintWriter output;
      if (selection == null) {
        println("Window was closed or the user hit cancel. Data was NOT saved!");
      } else {    // Error handling in case of bad filenames etc. try ... catch ???
        try {
          String filename = selection.getAbsolutePath();
          output = createWriter(filename);      
    
          //Header of File
          output.println("Time[secs],dataX,dataY");
    
          for (int i = 0; i < datax.size(); i++)
            output.println(dataTime.get(i)+","+datax.get(i)+","+datay.get(i)); // Write each patch as a line of comma separated values
    
          output.flush();
          output.close();
          println("Data saved at "+filename+". Exiting...\n\n\nNormal termination.");
          exit();
        }
        catch (Exception e) {
          println("Error while saving... " + e);
        }
      }
    }
    
    void serialEvent (Serial port) {  
      globalBuffer=myPort.readStringUntil('\n');
    }
    
    //Returns true when valid data is available
    boolean processInputData(String buff) {
    
      if (buff==null)
        return false;
    
      float[] mbuff=float(split(buff, ","));
    
      //Only process data with two tokens, discard anything else
      if (mbuff.length!=2)
        return false;
    
      index++;
      if (index==bSize)
        index=0;
    
      valuesx[index] = int(mbuff[0]);
      valuesy[index] = int(mbuff[1]);
    
      buff=null;  //Reset buffer for next data stream (IMPORTANT!!!)
      return true;
    }
    
    //Draws a line from previous index to current index
    void drawLine(int idx, int[] data, float LRange, float HRange, color cc) {
      float y1=map(data[idx-1], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      float y2=map(data[idx], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      stroke(cc);
      line(idx-1, y1, idx, y2);
    }
    
    //This function is for testing/debugging purposes
    //Loads data generated from a sine wave
    void loadArtificialData() {
      globalBuffer=str(index)+",";
      globalBuffer+=str(kMAX_DATA_VAL/2*sin(radians(index%width)*2*3.1415/0.8));
    
      println(globalBuffer);
    }
    
  • Type String[] of the last argument to method println(Object...) doesn't exactly match the vararg parameter type. Cast to Object[] to confirm the non-varargs invocation, or pass individual arguments of type Object for a varargs invocation. line 48

    The value of the parameter serial is not used line 139

  • I think I did not notice the duration, because the time resolution is one second.

  • Answer ✓

    @trinity

    I believe these are warnings. Your program still runs, doesn't it?

    Regarding duration, if you see line 96 and 126, I am saving time in milliseconds, so if your event is less than one second, you could still measured this time. Just take the time from the last entry and subtract the time from the first entry and then divide the number by 1000 to convert the duration to seconds. Please notice an error when I print the file header in line 123. It should be this instead: output.println("Time[msecs],dataX,dataY"); Notice the change from [secs] to [msecs].

    Kf

  • The data in the console is still being recorded with second resolution. I cannot see any decimal places. Should I make a change to line 67, 88, and/or 107?

  • Answer ✓

    The file you saved contains the data in milliseconds. After you run the file and save the data, open the file. The file should have three columns separated by commas. The data is saved as indicated in line 126. Last time stamp minus first time stamp should give you duration. Please send me the content of one of your files through a private message. To do so, click my ID name here in the forum and then click on the message button.

    To display the data in seconds with decimal points, please change line 88 to println("Data collection started at "+millis()/1000.0+" seconds"); Notice the decimal point for the 1000.0 divisor. Implement the same change in line 107.

    Kf

  • Yes, it is now showing millisecond time resolution. Thank you very much!!!

  • @trinity, you are very welcome. Good luck with the project!

    Kf

  • Sorry, but it appears that the time duration is less than 1ms. The sensor can measure down to 1 microseconds. Do you think this arduino can measure 0.1 ms or less?

  • I don't have experience to work with faster sampling rates in Arduinos. I found this link. Have you implemented changes in your arduino code to handle faster sampling rates?

    http://forum.arduino.cc/index.php?topic=6549.0

    Link to a general search: https://www.google.ca/search?q=arduino+sampling+rate&rlz=1C1CHWA_enCA688CA688&oq=arduino+sampling+rate&aqs=chrome..69i57j0l5.3753j0j7&sourceid=chrome&ie=UTF-8#q=arduino+leonardo+sampling+rate&*

    Kf

  • No, I did not try changing the code, because I was not sure where it should be modified.

  • Can you post your arduino code?

    Kf

  • edited February 27
    //REFERENCE: <a href="https://forum.processing.org/two/discussion/20685/etchasketch-code-modification#latest" target="_blank" rel="nofollow">https://forum.processing.org/two/discussion/20685/etchasketch-code-modification#latest</a>;
    //Leonardo specs: <a href="https://www.arduino.cc/en/Main/ArduinoBoardLeonardo" target="_blank" rel="nofollow">https://www.arduino.cc/en/Main/ArduinoBoardLeonardo</a>;
    //  Analog Inputs: A0-A5, A6 - A11 (on digital pins 4, 6, 8, 9, 10, and 12). The 
    //  Leonardo has 12 analog inputs, labeled A0 through A11, all of which can also 
    //  be used as digital i/o. Pins A0-A5 appear in the same locations as on the Uno; 
    //  inputs A6-A11 are on digital i/o pins 4, 6, 8, 9, 10, and 12 respectively. Each 
    //  analog input provide 10 bits of resolution (i.e. 1024 different values). By default 
    //  the analog inputs measure from ground to 5 volts, though is it possible to change 
    //  the upper end of their range using the AREF pin and the analogReference() function.
    //REFERENCE: <a href="https://forum.processing.org/two/discussion/35/how-to-get-the-filepath-from-selectinput" target="_blank" rel="nofollow">https://forum.processing.org/two/discussion/35/how-to-get-the-filepath-from-selectinput</a>;
    //REFERENCE: <a href="https://forum.processing.org/two/discussion/14595/simple-file-operations/p1" target="_blank" rel="nofollow">https://forum.processing.org/two/discussion/14595/simple-file-operations/p1</a>;
    
    
    import processing.serial.*;
    
    final int kMAX_DATA_VAL=1024; //10bit resolution
    final int kVERTICAL_MARGIN=int(kMAX_DATA_VAL*0.1);
    
    final int swTRIGGER = int(0.05*kMAX_DATA_VAL);  //Software trigger: 5% of input range
    
    IntList dataTime;  //Stores all data to be saved on file
    IntList datax;
    IntList datay;
    
    // Serial Port variables
    Serial myPort;
    
    int bSize;  //buffer size
    int index =-1;
    boolean collectDataNow=false;
    
    String globalBuffer;
    int[] valuesx;   //Data been displayed on screen
    int[] valuesy;
    
    
    void settings() {
      size(512, 512);
    }
    
    void setup()
    {
      bSize=width;
      valuesx = new int[bSize];
      valuesy = new int[bSize];
    
      noSmooth();
      println(Serial.list()); // use this to determine which serial port is your littleBits Arduino
      // EXAMPLE: myPort = new Serial(this, Serial.list()[ your serial port number ], 9600);
      myPort = new Serial(this, Serial.list()[0], 9600);
      myPort.bufferUntil('\n');
    
      dataTime=new IntList();
      datax=new IntList();
      datay=new IntList();
    
      background(87, 36, 124);
      strokeWeight(5);
    }
    
    
    void draw() {
    
      ////ENABLE NEXT LINE WHEN NO SERIAL EVENTS AVAILABLE
      //loadArtificialData();
    
      surface.setTitle("Index="+index+" time="+millis()/1000+" sec");
      boolean drawNow=processInputData(globalBuffer);
    
    
    
      if (drawNow==true) {
        //Reset background for new data wave
        if ((index)<=1) {
          background(87, 36, 124);
          return;
        }
     dataCollectionManager();
        drawLine(index, valuesx, 0, kMAX_DATA_VAL, color(255, 25, 25));   
        drawLine(index, valuesy, 0, kMAX_DATA_VAL, color(250, 205, 25));
      }
    }
    
    void dataCollectionManager() {
    
      if (valuesx[index] >swTRIGGER || valuesy[index] >swTRIGGER) {
        collectDataNow=true;
        println("Data collection started at "+millis()/1000.0+" seconds");
        println("Last valueX is " + valuesx[index] + " Over threshold?:" + (valuesx[index] >swTRIGGER) );
        println("Last valueY is " + valuesy[index] + " Over threshold?:" + (valuesy[index] >swTRIGGER) );
        println("Current threshold is " + swTRIGGER +" or" + nfp((5.0*swTRIGGER)/kMAX_DATA_VAL, 1, 3) + " volts");
      }
    
      //Store data only if incoming data is over threshold
      if (collectDataNow==true) {
        dataTime.append(millis());
        datax.append(valuesx[index]);
        datay.append(valuesy[index]);
      }
    
      //Stop data collection and exit only :
      // [1] After data collection was enabled and
      // [2] After input signal goes under threshold value
      if (collectDataNow==true) {
        if (!(valuesx[index] <swTRIGGER || valuesy[index] <swTRIGGER)) {
          noLoop();
          println("Data collection started at "+millis()/1000.0+" seconds");
          selectOutput("Save data as...", "saveData", dataFile(""));
        }
      }
    }
    
    void saveData(File selection) {
      PrintWriter output;
      if (selection == null) {
        println("Window was closed or the user hit cancel. Data was NOT saved!");
      } else {    // Error handling in case of bad filenames etc. try ... catch ???
        try {
          String filename = selection.getAbsolutePath();
          output = createWriter(filename);      
    
          //Header of File
          output.println("Time[msecs],dataX,dataY");
    
          for (int i = 0; i < datax.size(); i++)
            output.println(dataTime.get(i)+","+datax.get(i)+","+datay.get(i)); // Write each patch as a line of comma separated values
    
          output.flush();
          output.close();
          println("Data saved at "+filename+". Exiting...\n\n\nNormal termination.");
          exit();
        }
        catch (Exception e) {
          println("Error while saving... " + e);
        }
      }
    }
    
    void serialEvent (Serial port) {  
      globalBuffer=myPort.readStringUntil('\n');
    }
    
    //Returns true when valid data is available
    boolean processInputData(String buff) {
    
      if (buff==null)
        return false;
    
      float[] mbuff=float(split(buff, ","));
    
      //Only process data with two tokens, discard anything else
      if (mbuff.length!=2)
        return false;
    
      index++;
      if (index==bSize)
        index=0;
    
      valuesx[index] = int(mbuff[0]);
      valuesy[index] = int(mbuff[1]);
    
      buff=null;  //Reset buffer for next data stream (IMPORTANT!!!)
      return true;
    }
    
    //Draws a line from previous index to current index
    void drawLine(int idx, int[] data, float LRange, float HRange, color cc) {
      float y1=map(data[idx-1], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      float y2=map(data[idx], LRange, HRange, height-kVERTICAL_MARGIN, kVERTICAL_MARGIN);
      stroke(cc);
      line(idx-1, y1, idx, y2);
    }
    
    //This function is for testing/debugging purposes
    //Loads data generated from a sine wave
    void loadArtificialData() {
      globalBuffer=str(index)+",";
      globalBuffer+=str(kMAX_DATA_VAL/2*sin(radians(index%width)*2*3.1415/0.8));
    
      println(globalBuffer);
    }
    
  • @trinity I was referring to your arduino code. This is the processing code.

    Kf

  • edited February 27
    /*
     littleBits Arduino Module
     Original code by David Mellis http://www.arduino.cc/en/Tutorial/Graph
     created Jan 2009 by Trevor Shannon http://www.trevorshp.com/creations/etchasketch.htm
     modified Jan 2014 for littleBits by Kristin Salomon
     modified Mar 2014 for littleBits by Matt Richard
    
     _Etch-a-Sketch_
    
     What is supposed to happen:
     * Use two dimmers to draw in a Processing Etch-a-Sketch program
    
     Important Note:
     * You will need to download Processing to run the Processing sketch.
     Processing can be downloaded here: https://processing.org/download/?processing
    
     Circuit:
     * littleBits dimmer or slide dimmer on analog pin A0
     * littleBits dimmer or slide dimmer on analog pin A1
     * Optional:
     Use two littBits wire modules to properly orient the slide dimmers
     * Note:
     The following other input modules can be used: bend, light, pressure sensor
     However, their min and max range will need to be adjusted in Processing:
    
     This sketch includs the following commands:
     analogRead(pin);           pin = an analog pin
     delay(time);               time: milliseconds
     Serial.begin(baudrate);    baudrate = speed of serial communication
     Serial.read();             reads the incoming data from Serial port
     Serial.print();            sends data out the Serial port
     */
    
    int x;// this sensor reading is used to determine the x position of the Etch-a-Sketch draw head
    int y;// this sensor reading is used to determine the y position of the Etch-a-Sketch draw head
    
    void setup() {
      // initialize the serial communication
      Serial.begin(9600);
    
      // wait while littleBits Arduino module is not connected to Serial Port
      // this should not take long at all
      while(!Serial);
    }
    
    void loop() {
      // first we need to read the values from the sensors
    
      // read and assign the value of analog input A0 to the variable x
      x = analogRead(A0);
      // read and assign the value of analog input A1 to the variable y
      y = analogRead(A1);
    
      // next we need to send these values to Processing
      // Processing is expecting a particular format that looks like this "123,456 "
      // The format is: number | ',' | number | ' '
      // The ',' is used to tell the end of the first sensor value
      // The ' ' is used to determine the end of the second value and the packet as a whole
      Serial.print(x);
      Serial.print(",");
      Serial.println(y);
    
      // After we send the values we need to wait a moment for the Serial port
      delay(10);
    }
    
  • There is this more recent post that could be useful:

    http://yaab-arduino.blogspot.ca/2015/02/fast-sampling-from-analog-input.html

    One of the contributors shared his code: https://drive.google.com/file/d/0B6PxDhwK5tSJaTFka254WUk1NjA/view?usp=sharing

    I have a limited exposure to arduinos and trying to push it to higher sampling rates could be a tricky business if these samples codes don't work out of the box. I would suggest you ask this more technical questions on an Arduino forum.

    Of importance, you need to know about your sensors that you are reading from the arduino as that is probably the first thing one would like to know.

    Can you tell me more about your event that you are trying to measure? I am curious...

    Other reference links (more for the record):
    https://www.arduino.cc/en/Reference/PortManipulation
    http://forum.arduino.cc/index.php?topic=6549.15

    Kf

  • Essentially, an object is launched at a target, which is a pressure sensor. We are trying to determine the speed of the object using its change in momentum. I have placed foam behind the target; however, it does not stop the object completely. Accordingly, I have decided to make some changes to my device set-up in order to increase the contact time. I will add clay to the foam, in order to stop the object from moving backwards. This should also increase the contact time between the object and the pressure sensor.

Sign In or Register to comment.