|
|
(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)
1 #define PIN_ENC1 2
2 #define PIN_ENC2 3
3 #define PIN_EBTN 4
4 #define PIN_LED1 13
5 static boolean moving=false;
6 volatile unsigned int encValue = 0;
7 unsigned int encValueTracking = 1;
8 boolean enc1 = false;
9 boolean enc2 = false;
10
11 // Here I'm messing around with button press durations - we could go to town here!
12 enum pressDuration { reallyQuickPress, shortPress, normalPress, longPress, veryLongPress };
13 long presses[] = { 40, 150, 300, 800, 1400 };
14 char* pressText[]={"really quick press!", "short press", "normal press", "long press", "very looooooooong press"};
15
16 void setup()
17 {
18 pinMode(PIN_ENC1, INPUT);
19 pinMode(PIN_ENC2, INPUT);
20 pinMode(PIN_EBTN, INPUT);
21 pinMode(PIN_LED1, OUTPUT);
22 digitalWrite(PIN_ENC1, HIGH);
23 digitalWrite(PIN_ENC2, HIGH);
24 digitalWrite(PIN_EBTN, HIGH);
25 Serial.begin(9600);
26 menuIntro();
27 attachInterrupt(0, intrEncChange1, CHANGE);
28 attachInterrupt(1, intrEncChange2, CHANGE);
29 }
30
31 void intrEncChange1()
32 {
33 if(moving)
34 delay(1);
35 if(digitalRead(PIN_ENC1) == enc1)
36 return;
37 enc1 = !enc1;
38 if(enc1 && !enc2)
39 encValue += 1;
40 moving = false;
41 }
42
43 void intrEncChange2()
44 {
45 if(moving)
46 delay(1);
47 if(digitalRead(PIN_ENC2) == enc2)
48 return;
49 enc2 = !enc2;
50 if(enc2 && !enc1)
51 encValue -= 1;
52 moving = false;
53 }
54
55 void loop()
56 {
57 static unsigned long btnHeld = 0;
58 moving = true;
59 if(encValueTracking != encValue){
60 Serial.print("encValue: ");
61 Serial.println(encValue, DEC);
62 encValueTracking = encValue;
63 }
64 // Upon button press...
65 if((digitalRead(PIN_EBTN) == LOW) && !btnHeld){
66 btnHeld = millis();
67 digitalWrite(PIN_LED1, HIGH);
68 Serial.print("pressed selecting: ");
69 Serial.println(encValue, DEC);
70 }
71 // Upon button release...
72 if((digitalRead(PIN_EBTN) == HIGH) && btnHeld){
73 long t = millis();
74 t -= btnHeld;
75 digitalWrite(PIN_LED1, LOW);
76 int dur = veryLongPress;
77 for(int i = 0; i<= veryLongPress; i++){
78 if(t > presses[i])
79 continue;
80 dur = i;
81 break;
82 }
83
84 Serial.print("released selecting: ");
85 Serial.print(encValue, DEC);
86 Serial.print(" (after ");
87 Serial.print(t, DEC);
88 Serial.print(" ms = ");
89 Serial.print(pressText[dur]);
90 Serial.println(")");
91 btnHeld = 0;
92 }
93 }
94
95 void menuIntro()
96 {
97 Serial.println("Encoder Menu - blah, blah, blah");
98 }
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
1 #!/usr/bin/perl -w
2 use strict;
3 use IO::Handle;
4 autoflush STDOUT 1;
5 autoflush STDERR 1;
6 use tmstub;
7 use Tk;
8 #use Tk::FontDialog;
9 my $title = 'piduino menu';
10 my $version = "v0.2 2012-07-29";
11
12 my $mw = new MainWindow();
13 # PAL resolution 656x512@16
14 $mw->overrideredirect(1);
15 my ($scr_w, $scr_h) = $mw->maxsize();
16 t "maxsize says: $scr_w x $scr_h";
17 #~ $mw->geometry("656x512+0+0");
18 $mw->geometry("680x540+0+0");
19 #~ $mw->FullScreen;
20 #~ $mw->geometry(($mw->maxsize())[0] .'x'.($mw->maxsize())[1] . "+0+0");
21 my $c = $mw->Canvas(-bg => "yellow", -width => 680, -height => 540)->pack;
22 # in theory we should be able to scroll infinitely but let's stick to something reasonable
23 #$c->configure(-scrollregion => [0,0, 600, 400]);
24 my $font = $mw->fontCreate('menufont', -family => 'Village', -size => 38, -weight=>'bold');
25
26 my $menu_w = 400;
27 my $menu_h = 100;
28
29 # it don't work!
30 #~ invisible_cursor();
31
32 # warp pointer tests...
33 $mw->after(100, sub {warp(20,20)});
34
35 sub warp {
36 my($x, $y) = @_;
37 t "warp pointer to $x $y";
38 $c->focus;
39 $mw->eventGenerate("<Motion>",
40 -when => 'head',
41 -x => $x, -y => $y, -warp => 1
42 );
43 }
44
45 # keybinding tests...
46 $mw->bind('<KeyPress>' => \&print_keysym);
47
48 sub print_keysym {
49 my($widget) = @_;
50 my $e = $widget->XEvent; # get event object
51 my($keysym_text, $keysym_decimal) = ($e->K, $e->N);
52 print "keysym=$keysym_text, numeric=$keysym_decimal\n";
53 }
54
55 # hide cursor
56 sub invisible_cursor {
57 my $bitmapfile = Tk->findINC('trans_cur.xbm');
58 my $maskfile = Tk->findINC('trans_cur.mask');
59 $mw->configure(-cursor => [ '@' . $bitmapfile, $maskfile, 'black', 'white' ] );
60 }
61
62 # handy font chooser
63 #~ $mw->Button(
64 #~ -font => $font,
65 #~ -text => "Font",
66 #~ -command => sub{choose_font()},
67 #~ )->pack();
68
69 # debug grid...
70 $c->createGrid(0, 0, 10, 10);
71 $c->createGrid(0, 0, 50, 50, -lines => 1, -dash => '-.');
72 $c->createGrid(0, 0, 100, 100, -width => 3, -lines => 1);
73
74 # TODO - load settings from file
75 menubox("Music", 0, 0);
76 menubox("Controls", 0, 1);
77 menubox("Vehicle", 0, 2);
78 menubox("System", 0, 3);
79 menubox("Settings", 0, 3);
80 # menubox("More...", 1, 0);
81
82 #~ music selection
83 #~ playlists
84 #~ artists by name
85 #~ albums
86 #~ directories
87
88 #~ controls
89 #~ volume
90 #~ bass
91 #~ treble
92 #~ balance
93 #~ system reboot
94 #~ system shutdown
95 #~ app quit
96 #~ app restart
97
98
99
100 sub menubox {
101 my($t, $mx, $my) = @_;
102 my $x1 = $mx * $menu_w;
103 my $y1 = $my * $menu_h;
104 my $x2 = $x1 + $menu_w;
105 my $y2 = $y1 + $menu_h;
106
107 $c->createRectangle($x1, $y1, $x2, $y2,
108 -fill => "blue",
109 -activefill => "green",
110 -outline => "green",
111 -activeoutline => "orange",
112 );
113 $c->createText($x1 + ($menu_w/2), $y1+ ($menu_h/2),
114 -text => "$t",
115 -font => $font,
116 -fill => "black",
117 );
118 }
119
120
121 =for docs
122
123 options:
124
125 * canvas width and height to cover the whole menu system
126 * sub-menus can't all be visible simultaneously so we'd have to hide them
127 * group items together
128
129 do top level menu first
130
131 menu level zero at 0,0
132 menu level one appears at 1*menu_w, selected item * menu_h
133
134
135
136 =cut
137
138
139
140
141 sub choose_font
142 {
143 # t $mw->GetDescriptiveFontName($font);
144
145 # my $f = $mw->FontDialog->Show(
146 #-initfont => $font,
147 #-nicefont => 1
148 # );
149 # return unless defined $f;
150 # $mw->RefontTree(-font => $f, -canvas => 1);
151 # my $d = $mw->GetDescriptiveFontName($f);
152 # t $d;
153 # $font = $f;
154
155 }
156
157
158 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
1 #include <PinChangeInt.h> // necessary otherwise we get undefined reference errors.
2 #include <AdaEncoder.h>
3
4 #define a_PINA 10
5 #define a_PINB 11
6 #define BTN 9
7 #define LED 13
8 #define b_PINA A3
9 #define b_PINB A4
10
11 int8_t clicks=0;
12 char id=0;
13
14 void setup()
15 {
16 Serial.begin(9600);
17 Serial.println("---------------------------------------");
18 pinMode(BTN, INPUT);
19 pinMode(LED, OUTPUT);
20 digitalWrite(BTN, HIGH);
21 AdaEncoder::addEncoder('a', a_PINA, a_PINB);
22 // AdaEncoder::addEncoder('b', b_PINA, b_PINB);
23 }
24
25 void loop()
26 {
27 static unsigned long btnHeld = 0;
28 encoder *thisEncoder;
29 thisEncoder=AdaEncoder::genie(&clicks, &id);
30 if (thisEncoder != NULL) {
31 thisEncoder=AdaEncoder::getFirstEncoder();
32
33 Serial.print(id);
34 Serial.print(':');
35 Serial.print(clicks, DEC);
36 if (clicks > 0) {
37 Serial.println(" UP");
38 }
39 if (clicks < 0) {
40 Serial.println(" DOWN");
41 }
42 }
43 // Upon button press...
44 if((digitalRead(BTN) == LOW) && !btnHeld){
45 btnHeld = millis();
46 digitalWrite(LED, HIGH);
47 Serial.println("pressed");
48
49 }
50 // Upon button release...
51 if((digitalRead(BTN) == HIGH) && btnHeld){
52 long t = millis();
53 digitalWrite(LED, LOW);
54 Serial.print("released: (after ");
55 t -= btnHeld;
56 Serial.print(t, DEC);
57 Serial.println(" ms)");
58
59 btnHeld = 0;
60 }
61 }