Thursday, December 31, 2015

Touchboard scrolling

Using proximity sensor for scrolling documents
What

For Christmas I bought myself a touchboard from bareconductive. It's a board with Atmel microprocessor ATMega32U4, 12 Analog inputs, 20 digital pins etc. It's from arduino family so you can use arduino IDE for your projects. This board comes with special conductive paint called "bare-paint". In combination with 12 analog inputs you can create touch or proximity sensors.

First touch

When I first tried the proximity sensor as a volume setting, it was like some kind of a magic, haha. I was positively surprised about the proximity precision. There SDK comes up with about 10 examples. The libraries are quite good and simple. You can do anything with a few lines of code. If you want to use the board for sound effects there are two options - mp3 or midi. MP3 files can be replaced on miroSD card and for midi, there are more than 100 musical instruments. To toggle between midi and mp3 version you have to join two connectors to the board.

First exercise

My main idea was to build some kind of a music table, but while getting into it I first tried scrolling. Scrolling can be done with keyboard or mouse. The only disadvantage of mouse scrolling is that you need to click after scrolling if you want to continue writing so I prefer the keyboard version.

I used HID library which allows you to trigger keyboard/mouse controls. In this exercise I only used one electrode E0 on touch board for proximity. The logic behind is that, when user touches the sensor first time, scrolling is enabled. After you scroll to your position you have to touch sensor again to disable scrolling. The area above the sensor is divided into two parts, the closer is for direction down, the opposite one is for up direction.

The filtering from "proximity example" is used there, that smooths input and set values to range <0-50>. Second filter is function that dynamically changes the delay for keyboard execution speed. In other words, if your hand is closer to the sensor, it scrolls faster.

Result

I tried this kind of a scrolling for a few hours, it wasn't helping me, it just slowed me down. But when I showed it to my friend's younger brother he almost immediately came up with a game who can scroll to a specific line faster.


       

// compiler error handling
#include "Compiler_Errors.h"

// touch  includes
#include 
#include 
#define MPR121_ADDR 0x5C
#define MPR121_INT 4

#include 
#include 

// serial rate
#define baudRate 57600

#define SCROLL_ELECTRODE 0

// define LED_BUILTIN for older versions of Arduino
#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

// mapping and filter definitions
#define LOW_DIFF 0
#define HIGH_DIFF 50
#define filterWeight 0.3f // 0.0f to 1.0f - higher value = more smoothing
float lastProx = 0;

#define SCROLL_ENABLED 1
#define SCROLL_DISABLED 0

#define SCROLL_MOUSE 0
#define SCROLL_KEYBOARD 1

int scrollingState = SCROLL_DISABLED;
int lastReading = 0;
int scrollType = SCROLL_KEYBOARD;

void setup() {
  Serial.begin(baudRate);
  pinMode(LED_BUILTIN, OUTPUT);
  Wire.begin();

  if (!MPR121.begin(MPR121_ADDR)) {
    Serial.println("error setting up MPR121");
    switch (MPR121.getError()) {
      case NO_ERROR:
        Serial.println("no error");
        break;
      case ADDRESS_UNKNOWN:
        Serial.println("incorrect address");
        break;
      case READBACK_FAIL:
        Serial.println("readback failure");
        break;
      case OVERCURRENT_FLAG:
        Serial.println("overcurrent on REXT pin");
        break;
      case OUT_OF_RANGE:
        Serial.println("electrode out of range");
        break;
      case NOT_INITED:
        Serial.println("not initialised");
        break;
      default:
        Serial.println("unknown error");
        break;
    }
    while (1);
  }

  MPR121.updateAll();

  Keyboard.begin();
  Mouse.begin();
  setupProximity();
}

void setupProximity() {
  // slow down some of the MPR121 baseline filtering to avoid
  // filtering out slow hand movements
  MPR121.setRegister(NHDF, 0x01); //noise half delta (falling)
  MPR121.setRegister(FDLF, 0x3F); //filter delay limit (falling)
}

void loop() {
  MPR121.updateAll();
  proximitySensor(SCROLL_ELECTRODE);
}

int getScrollingState() {
  return scrollingState;
}

void toggleScrooling() {
  if (scrollingState == SCROLL_ENABLED) {
    scrollingState = SCROLL_DISABLED;
    Serial.println("Scrolling disabled");
    digitalWrite(LED_BUILTIN, LOW);
    // when scrool by mosewheel ends where to click ?
    //     if (scrollType == SCROLL_MOUSE) {
    //      Mouse.click();
    //     }
  } else {
    scrollingState = SCROLL_ENABLED;
    Serial.println("Scrolling enabled");
    digitalWrite(LED_BUILTIN, HIGH);
  }
}

void proximitySensor(int electrode) {

  // read the difference between the measured baseline and the measured continuous data
  int reading = MPR121.getBaselineData(electrode) - MPR121.getFilteredData(electrode);

  if (lastReading < 100 && reading > 100) {
    toggleScrooling();
  }
  lastReading = reading;

  // print out the reading value for debug
  Serial.print("Proximity electrode: ");
  Serial.println(reading);

  if (getScrollingState() == SCROLL_ENABLED) {

    // constrain the reading between our low and high mapping values
    unsigned int prox = constrain(reading, LOW_DIFF, HIGH_DIFF);

    // implement a simple (IIR lowpass) smoothing filter
    lastProx = (filterWeight * lastProx) + ((1 - filterWeight) * (float)prox);
    // Serial.print("lastProx: "); Serial.println(lastProx);

    if (lastProx > 0) {

      if (lastProx < (HIGH_DIFF * 0.4) ) {
        scroll(-1, lastProx);
        // Serial.println("UP");

      } else if (lastProx > (HIGH_DIFF * 0.6) ) {
        scroll(1, lastProx);
        // Serial.println("DOWN");

      }
    }
  }

}

/*
   scroll logic - 2 levels
   100-60% proximity => direction down
   0-40% porximity => direction up
*/
void scroll(int direction, float prox) {

  if (prox > HIGH_DIFF / 2) {
    prox = abs(prox - HIGH_DIFF);
  }

  // prox is now between 0-25

  // do some smooth filter
  prox =  prox * (prox / 4) * 1.3;

  if (scrollType == SCROLL_MOUSE) {
    Mouse.move(0, 0, 1 * direction * -1);
    delay(prox);

  } else if (scrollType == SCROLL_KEYBOARD) {

     if (directi 1) {
      Keyboard.press(KEY_DOWN_ARROW);
    } else {
      Keyboard.press(KEY_UP_ARROW);
    }
    Keyboard.releaseAll();
    delay(prox);

  }
}

 

  




Share: