|
|
(2 intermediate revisions by one other user not shown) |
Line 142: |
Line 142: |
| my ($scr_w, $scr_h) = $mw->maxsize(); | | my ($scr_w, $scr_h) = $mw->maxsize(); |
| t "maxsize says: $scr_w x $scr_h"; | | t "maxsize says: $scr_w x $scr_h"; |
| $mw->geometry("656x512+0+0"); | | #~ $mw->geometry("656x512+0+0"); |
| | | $mw->geometry("680x540+0+0"); |
| | #~ $mw->FullScreen; |
| #~ $mw->geometry(($mw->maxsize())[0] .'x'.($mw->maxsize())[1] . "+0+0"); | | #~ $mw->geometry(($mw->maxsize())[0] .'x'.($mw->maxsize())[1] . "+0+0"); |
| my $c = $mw->Canvas(-bg => "red", -width => 650, -height => 400)->pack; | | my $c = $mw->Canvas(-bg => "yellow", -width => 680, -height => 540)->pack; |
| # in theory we should be able to scroll infinitely but let's stick to something reasonable | | # in theory we should be able to scroll infinitely but let's stick to something reasonable |
| #$c->configure(-scrollregion => [0,0, 600, 400]); | | #$c->configure(-scrollregion => [0,0, 600, 400]); |
Line 153: |
Line 154: |
| my $menu_h = 100; | | my $menu_h = 100; |
|
| |
|
| | # it don't work! |
| | #~ invisible_cursor(); |
| | |
| | # warp pointer tests... |
| | $mw->after(100, sub {warp(20,20)}); |
| | |
| | sub warp { |
| | my($x, $y) = @_; |
| | t "warp pointer to $x $y"; |
| | $c->focus; |
| | $mw->eventGenerate("<Motion>", |
| | -when => 'head', |
| | -x => $x, -y => $y, -warp => 1 |
| | ); |
| | } |
| | |
| | # keybinding tests... |
| | $mw->bind('<KeyPress>' => \&print_keysym); |
| | |
| | sub print_keysym { |
| | my($widget) = @_; |
| | my $e = $widget->XEvent; # get event object |
| | my($keysym_text, $keysym_decimal) = ($e->K, $e->N); |
| | print "keysym=$keysym_text, numeric=$keysym_decimal\n"; |
| | } |
| | |
| | # hide cursor |
| | sub invisible_cursor { |
| | my $bitmapfile = Tk->findINC('trans_cur.xbm'); |
| | my $maskfile = Tk->findINC('trans_cur.mask'); |
| | $mw->configure(-cursor => [ '@' . $bitmapfile, $maskfile, 'black', 'white' ] ); |
| | } |
|
| |
|
| # handy font chooser | | # handy font chooser |
| #~ $mw->Button( | | #~ $mw->Button( |
| #~ -font => $font,
| | #~ -font => $font, |
| #~ -text => "Font",
| | #~ -text => "Font", |
| #~ -command => sub{choose_font()},
| | #~ -command => sub{choose_font()}, |
| #~ )->pack(); | | #~ )->pack(); |
|
| |
|
Line 193: |
Line 226: |
|
| |
|
| sub menubox { | | sub menubox { |
| my($t, $mx, $my) = @_;
| | my($t, $mx, $my) = @_; |
| my $x1 = $mx * $menu_w;
| | my $x1 = $mx * $menu_w; |
| my $y1 = $my * $menu_h;
| | my $y1 = $my * $menu_h; |
| my $x2 = $x1 + $menu_w;
| | my $x2 = $x1 + $menu_w; |
| my $y2 = $y1 + $menu_h;
| | my $y2 = $y1 + $menu_h; |
|
| |
|
| $c->createRectangle($x1, $y1, $x2, $y2,
| | $c->createRectangle($x1, $y1, $x2, $y2, |
| -fill => "blue",
| | -fill => "blue", |
| -activefill => "green",
| | -activefill => "green", |
| -outline => "green",
| | -outline => "green", |
| -activeoutline => "orange",
| | -activeoutline => "orange", |
| );
| | ); |
| $c->createText($x1 + ($menu_w/2), $y1+ ($menu_h/2),
| | $c->createText($x1 + ($menu_w/2), $y1+ ($menu_h/2), |
| -text => "$t",
| | -text => "$t", |
| -font => $font,
| | -font => $font, |
| -fill => "black",
| | -fill => "black", |
| );
| | ); |
| } | | } |
|
| |
|
Line 256: |
Line 289: |
|
| |
|
|
| |
|
| | OK, using adaencoder on the Pi-arduino bridge! The Arduino is just saying what it sees over serial. |
| | |
| | <div style ="height:300px;overflow-x:hidden;overflow-y:auto;border: 4px solid green;"> |
| | '''Raspberry Pi menu for rotary encoder''' |
| | <syntaxhighlight lang="cpp" line="GESHI_FANCY_LINE_NUMBERS" enclose="div"> |
| | #include <PinChangeInt.h> // necessary otherwise we get undefined reference errors. |
| | #include <AdaEncoder.h> |
| | |
| | #define a_PINA 10 |
| | #define a_PINB 11 |
| | #define BTN 9 |
| | #define LED 13 |
| | #define b_PINA A3 |
| | #define b_PINB A4 |
| | |
| | int8_t clicks=0; |
| | char id=0; |
| | |
| | void setup() |
| | { |
| | Serial.begin(9600); |
| | Serial.println("---------------------------------------"); |
| | pinMode(BTN, INPUT); |
| | pinMode(LED, OUTPUT); |
| | digitalWrite(BTN, HIGH); |
| | AdaEncoder::addEncoder('a', a_PINA, a_PINB); |
| | // AdaEncoder::addEncoder('b', b_PINA, b_PINB); |
| | } |
| | |
| | void loop() |
| | { |
| | static unsigned long btnHeld = 0; |
| | encoder *thisEncoder; |
| | thisEncoder=AdaEncoder::genie(&clicks, &id); |
| | if (thisEncoder != NULL) { |
| | thisEncoder=AdaEncoder::getFirstEncoder(); |
|
| |
|
| | Serial.print(id); |
| | Serial.print(':'); |
| | Serial.print(clicks, DEC); |
| | if (clicks > 0) { |
| | Serial.println(" UP"); |
| | } |
| | if (clicks < 0) { |
| | Serial.println(" DOWN"); |
| | } |
| | } |
| | // Upon button press... |
| | if((digitalRead(BTN) == LOW) && !btnHeld){ |
| | btnHeld = millis(); |
| | digitalWrite(LED, HIGH); |
| | Serial.println("pressed"); |
|
| |
|
| moving the cursor with Perl Tk
| | } |
| | // Upon button release... |
| | if((digitalRead(BTN) == HIGH) && btnHeld){ |
| | long t = millis(); |
| | digitalWrite(LED, LOW); |
| | Serial.print("released: (after "); |
| | t -= btnHeld; |
| | Serial.print(t, DEC); |
| | Serial.println(" ms)"); |
| | |
| | btnHeld = 0; |
| | } |
| | } |
| | |
| | </syntaxhighlight> |
| | </div> |
I have recovered a number of ALPS rotary encoders from discarded industrial CRT monitors. I'm working on building a user interface menu system that makes use of the rotary encoders as the sole input technique.
Rotary Encoder Test Sketch With Interrupts (non-PWM)
#define PIN_ENC1 2
#define PIN_ENC2 3
#define PIN_EBTN 4
#define PIN_LED1 13
static boolean moving=false;
volatile unsigned int encValue = 0;
unsigned int encValueTracking = 1;
boolean enc1 = false;
boolean enc2 = false;
// Here I'm messing around with button press durations - we could go to town here!
enum pressDuration { reallyQuickPress, shortPress, normalPress, longPress, veryLongPress };
long presses[] = { 40, 150, 300, 800, 1400 };
char* pressText[]={"really quick press!", "short press", "normal press", "long press", "very looooooooong press"};
void setup()
{
pinMode(PIN_ENC1, INPUT);
pinMode(PIN_ENC2, INPUT);
pinMode(PIN_EBTN, INPUT);
pinMode(PIN_LED1, OUTPUT);
digitalWrite(PIN_ENC1, HIGH);
digitalWrite(PIN_ENC2, HIGH);
digitalWrite(PIN_EBTN, HIGH);
Serial.begin(9600);
menuIntro();
attachInterrupt(0, intrEncChange1, CHANGE);
attachInterrupt(1, intrEncChange2, CHANGE);
}
void intrEncChange1()
{
if(moving)
delay(1);
if(digitalRead(PIN_ENC1) == enc1)
return;
enc1 = !enc1;
if(enc1 && !enc2)
encValue += 1;
moving = false;
}
void intrEncChange2()
{
if(moving)
delay(1);
if(digitalRead(PIN_ENC2) == enc2)
return;
enc2 = !enc2;
if(enc2 && !enc1)
encValue -= 1;
moving = false;
}
void loop()
{
static unsigned long btnHeld = 0;
moving = true;
if(encValueTracking != encValue){
Serial.print("encValue: ");
Serial.println(encValue, DEC);
encValueTracking = encValue;
}
// Upon button press...
if((digitalRead(PIN_EBTN) == LOW) && !btnHeld){
btnHeld = millis();
digitalWrite(PIN_LED1, HIGH);
Serial.print("pressed selecting: ");
Serial.println(encValue, DEC);
}
// Upon button release...
if((digitalRead(PIN_EBTN) == HIGH) && btnHeld){
long t = millis();
t -= btnHeld;
digitalWrite(PIN_LED1, LOW);
int dur = veryLongPress;
for(int i = 0; i<= veryLongPress; i++){
if(t > presses[i])
continue;
dur = i;
break;
}
Serial.print("released selecting: ");
Serial.print(encValue, DEC);
Serial.print(" (after ");
Serial.print(t, DEC);
Serial.print(" ms = ");
Serial.print(pressText[dur]);
Serial.println(")");
btnHeld = 0;
}
}
void menuIntro()
{
Serial.println("Encoder Menu - blah, blah, blah");
}
multiple encoders
Try out http://code.google.com/p/adaencoder/ and PinChangeInt library (http://code.google.com/p/arduino-pinchangeint/) see...
http://arduino.cc/playground/Main/RotaryEncoders#Example14
See if it can cope with two encoders at rapid rate usage.
OK, working well --Michael Erskine 18:10, 29 July 2012 (EST)
When using the Raspberry Pi as the menu display system we need to do a few things to a stock raspbian install...
- auto login and start X: use option in raspi-config
- switch off screen blanking: /etc/lightdm/lightdm.conf in SeatDefaults section "xserver-command=X -s 0 -dpms"
- auto start application
Here I'm building a Perl Tk menu application prototype using Tk::Canvas. I haven't yet wired up the serial as I'm just trying out some graphic styles on the small PAL LCD screen.
Raspberry Pi menu for rotary encoder
#!/usr/bin/perl -w
use strict;
use IO::Handle;
autoflush STDOUT 1;
autoflush STDERR 1;
use tmstub;
use Tk;
#use Tk::FontDialog;
my $title = 'piduino menu';
my $version = "v0.2 2012-07-29";
my $mw = new MainWindow();
# PAL resolution 656x512@16
$mw->overrideredirect(1);
my ($scr_w, $scr_h) = $mw->maxsize();
t "maxsize says: $scr_w x $scr_h";
#~ $mw->geometry("656x512+0+0");
$mw->geometry("680x540+0+0");
#~ $mw->FullScreen;
#~ $mw->geometry(($mw->maxsize())[0] .'x'.($mw->maxsize())[1] . "+0+0");
my $c = $mw->Canvas(-bg => "yellow", -width => 680, -height => 540)->pack;
# in theory we should be able to scroll infinitely but let's stick to something reasonable
#$c->configure(-scrollregion => [0,0, 600, 400]);
my $font = $mw->fontCreate('menufont', -family => 'Village', -size => 38, -weight=>'bold');
my $menu_w = 400;
my $menu_h = 100;
# it don't work!
#~ invisible_cursor();
# warp pointer tests...
$mw->after(100, sub {warp(20,20)});
sub warp {
my($x, $y) = @_;
t "warp pointer to $x $y";
$c->focus;
$mw->eventGenerate("<Motion>",
-when => 'head',
-x => $x, -y => $y, -warp => 1
);
}
# keybinding tests...
$mw->bind('<KeyPress>' => \&print_keysym);
sub print_keysym {
my($widget) = @_;
my $e = $widget->XEvent; # get event object
my($keysym_text, $keysym_decimal) = ($e->K, $e->N);
print "keysym=$keysym_text, numeric=$keysym_decimal\n";
}
# hide cursor
sub invisible_cursor {
my $bitmapfile = Tk->findINC('trans_cur.xbm');
my $maskfile = Tk->findINC('trans_cur.mask');
$mw->configure(-cursor => [ '@' . $bitmapfile, $maskfile, 'black', 'white' ] );
}
# handy font chooser
#~ $mw->Button(
#~ -font => $font,
#~ -text => "Font",
#~ -command => sub{choose_font()},
#~ )->pack();
# debug grid...
$c->createGrid(0, 0, 10, 10);
$c->createGrid(0, 0, 50, 50, -lines => 1, -dash => '-.');
$c->createGrid(0, 0, 100, 100, -width => 3, -lines => 1);
# TODO - load settings from file
menubox("Music", 0, 0);
menubox("Controls", 0, 1);
menubox("Vehicle", 0, 2);
menubox("System", 0, 3);
menubox("Settings", 0, 3);
# menubox("More...", 1, 0);
#~ music selection
#~ playlists
#~ artists by name
#~ albums
#~ directories
#~ controls
#~ volume
#~ bass
#~ treble
#~ balance
#~ system reboot
#~ system shutdown
#~ app quit
#~ app restart
sub menubox {
my($t, $mx, $my) = @_;
my $x1 = $mx * $menu_w;
my $y1 = $my * $menu_h;
my $x2 = $x1 + $menu_w;
my $y2 = $y1 + $menu_h;
$c->createRectangle($x1, $y1, $x2, $y2,
-fill => "blue",
-activefill => "green",
-outline => "green",
-activeoutline => "orange",
);
$c->createText($x1 + ($menu_w/2), $y1+ ($menu_h/2),
-text => "$t",
-font => $font,
-fill => "black",
);
}
=for docs
options:
* canvas width and height to cover the whole menu system
* sub-menus can't all be visible simultaneously so we'd have to hide them
* group items together
do top level menu first
menu level zero at 0,0
menu level one appears at 1*menu_w, selected item * menu_h
=cut
sub choose_font
{
# t $mw->GetDescriptiveFontName($font);
# my $f = $mw->FontDialog->Show(
#-initfont => $font,
#-nicefont => 1
# );
# return unless defined $f;
# $mw->RefontTree(-font => $f, -canvas => 1);
# my $d = $mw->GetDescriptiveFontName($f);
# t $d;
# $font = $f;
}
MainLoop();
OK, using adaencoder on the Pi-arduino bridge! The Arduino is just saying what it sees over serial.
Raspberry Pi menu for rotary encoder
#include <PinChangeInt.h> // necessary otherwise we get undefined reference errors.
#include <AdaEncoder.h>
#define a_PINA 10
#define a_PINB 11
#define BTN 9
#define LED 13
#define b_PINA A3
#define b_PINB A4
int8_t clicks=0;
char id=0;
void setup()
{
Serial.begin(9600);
Serial.println("---------------------------------------");
pinMode(BTN, INPUT);
pinMode(LED, OUTPUT);
digitalWrite(BTN, HIGH);
AdaEncoder::addEncoder('a', a_PINA, a_PINB);
// AdaEncoder::addEncoder('b', b_PINA, b_PINB);
}
void loop()
{
static unsigned long btnHeld = 0;
encoder *thisEncoder;
thisEncoder=AdaEncoder::genie(&clicks, &id);
if (thisEncoder != NULL) {
thisEncoder=AdaEncoder::getFirstEncoder();
Serial.print(id);
Serial.print(':');
Serial.print(clicks, DEC);
if (clicks > 0) {
Serial.println(" UP");
}
if (clicks < 0) {
Serial.println(" DOWN");
}
}
// Upon button press...
if((digitalRead(BTN) == LOW) && !btnHeld){
btnHeld = millis();
digitalWrite(LED, HIGH);
Serial.println("pressed");
}
// Upon button release...
if((digitalRead(BTN) == HIGH) && btnHeld){
long t = millis();
digitalWrite(LED, LOW);
Serial.print("released: (after ");
t -= btnHeld;
Serial.print(t, DEC);
Serial.println(" ms)");
btnHeld = 0;
}
}