This tutorial describes how to use the HiTechnik Trike Vehicle for some simple experiments with pbLua. The Trike model makes a great starter vehicle because it’s easy to build and can be controlled in lots of interesting ways.
By the end of this tutorial, you will have written some very simple functions that can be used as building blocks for other robots. You’ll be able to start and stop a pbLua program from the front panel, make the Trike move forward and backward at different speeds, as well as spin the Trike to the left and right to make turns.
You should be familiar with connecting to the NXT either with a USB cable, or over Bluetooth. Remember that pbLua is replacement firmware and is designed to work with minimal requirements with lots of different computers and operating systems.
As a reminder, there is an excellent online version of Programming in Lua that is a complete description of Lua along with many programming examples, and an even better idea is to purchase the latest Programming in Lua book .
If you’re starting here, then you’re probably thinking – Where’s the Integrated Development Environment (IDE) for pbLua? Surprise, there isn’t one. The reason is that the complexity of an IDE gets in the way of getting started quickly, and it makes it harder to use a programming language on lots of different computers.
The only tools you’ll need for programming with pbLua on any computer is a text editor and a terminal emulator. The text editor part is easy, that’s Notepad for Windows users and something else for the rest of you. OK, Notepad is a terrible editor for programming, but it’s good enough until you find one you like.
The terminal emulator isn’t that hard to figure out either. That’s Hyperterminal for Windows users, and screen or minicom for Mac and Linux people. All your terminal emulator has to be able to do is connect to a serial device and send text to it. pbLua connects as a simple COM or serial device using either the USB or Bluetooth capabilities of the NXT.
So how do you get programs to run on the NXT using pbLua? Just type it in yourself, or cut the text from the editor and paste it into the terminal window. Remember, pbLua actually compiles your text right on the brick and executes it as well. There is no compiler or any other complicated software to master on your computer.
With all of the new netbooks and slate computers out there, you’ll be able to program your NXT running pbLua without a traditional computer at all! OK, let’s move on to the first step – recognizing key presses.
One of the nice things that NXT-G provides is an easy to use interface between your programs and the keyboard on the NXT. pbLua is a bit more… primitive. You’ll need to write code to handle switch presses yourself, but even something this simple has a lesson to teach.
When you boot up the NXT, you get a message saying that it’s waiting for the internal Bluetooth system to supply the NXT with a serial number. Once that’s done you get to choose between the USB and Bluetooth consoles. Along the bottom of the screen, you’ll see a field labeled “Button:x“. Pressing the orange button selects whatever console is highlighted. The keys are numbered according to this table:
|0x0||0||No Button Pressed|
|0x1||1||Grey Button Pressed|
|0x2||2||Right Button Pressed|
|0x4||4||Left Button Pressed|
|0x8||8||Orange Button Pressed|
The pbLua function that reads the front panel switches is state = nxt.ButtonRead( ). It simply returns the number according to the switch that is pressed. When no switch is pressed, the function returns 0. So if we want to wait until a specific switch is pressed, we just do this:
-- Simple test code for reading orange switch function TestButton() print( "Waiting for keypress" ) -- Now spin here until someone presses the orange key repeat until( 8 == nxt.ButtonRead() ) -- And spin here until someone presses the ornage key again repeat print( "Running main routine" ) until( 8 == nxt.ButtonRead() ) print( "Done" ) end -- Running the test TestButton()
Learning to use the front panel switches effectively should be pretty simple. What could possibly go wrong with this simple piece of code? According to the comments, it should do the following:
- Print “Waiting for keypress” on the console and then wait for the orange key to be pressed
- Print “Running main routine” on the console in a loop until the orange key is pressed
- Print “Done”
So that happens when you execute TestKey()? Try it and think about what you see…
Obviously, something is wrong. The function does not wait seem to wait for the second keypress. It prints “Running main routine” once then prints “Done” right away. I’ll give you another few seconds to think about it…
OK, time’s up. The reason the TestKey() function seems to not work is that we did not wait for the key to be released before we check for a key press! The pbLua code executed the “Running main routine” print so quickly that by the time we get to the end of the loop, the orange key is still pressed!
For simple routines, I usually use a framework similar to this:
- Print “Waiting for keypress” on the console and then wait for the orange key to be pressed and released
- Print “Running main routine” on the console in a loop until the orange key is pressed
- Print “Done”
-- A better framework for reading the orange switch function TestButton() print( "Waiting for keypress" ) -- Now spin here until someone presses and releases the orange key local oldKey = 0; local newKey = 0; repeat oldKey = newKey newKey = nxt.ButtonRead() until( oldKey == 8 and newKey == 0 ) -- And spin here until someone presses the orange key again repeat print( "Running main routine" ) until( 8 == nxt.ButtonRead() ) print( "Done" ) end -- Running the test TestButton()
Now when you run TestKey() it should work as expected, and we’re ready to build a little library of routines to make the Trike move.
Now that we have a simple way of starting and stopping a program (or function) let’s get the Trike moving. The simplest possible thing we can do is to write a little routine to turn on the motors , wait for another keypress, and then stop the motors. I won’t put you through the embarrassment of trying a program that forgets to turn off the motors when it’s done. Let’s just say that the first time I wrote this routine, the Trike ended up going forward longer than it should have.
Here’s that little routine, fully commented:
-- Move the Trike forward for one second...then stop. Note that we're using -- our orange switch framework more sensibly. You can pass a function to the -- TestButton() function now! function TestTrike( doThis ) print( "Waiting for keypress" ) -- Now spin here until someone presses and releases the orange key local oldKey = 0; local newKey = 0; repeat oldKey = newKey newKey = nxt.ButtonRead() until( oldKey == 8 and newKey == 0 ) doThis() print( "Done" ) end -- Here's the actual test function function MoveForward( ) local ticks = nxt.TimerRead() -- Turn the motors on -- nxt.OutputSetSpeed(2,32,80) nxt.OutputSetSpeed(3,32,80) -- Stay here for 1 second (1000 ticks) -- repeat -- this space intentionally left blank until( ticks+1000 < nxt.TimerRead() ) -- Turn the motors off -- nxt.OutputSetSpeed(2,0,0) nxt.OutputSetSpeed(3,0,0) end -- Running the test TestTrike( MoveForward )
So moving the trike forward is not too hard, and neither is stopping it. If we were to break everything out into the 5 functions that we need for controlling the Trike, it would probably look like this:
- Move Forward
- Move Backward
- Spin Clockwise
- Spin Counterclockwise
While it would be nice to be able to tell the trike how far to move or spin, that’s not what we’re looking to do right now. We just want the basics, and the first pass might look something like this:
-- A first pass at functions to move the Trike -- function TrikeForward( speed ) nxt.OutputSetSpeed(2,32, speed) nxt.OutputSetSpeed(3,32, speed) end function TrikeReverse( speed ) nxt.OutputSetSpeed(2,32,-speed) nxt.OutputSetSpeed(3,32,-speed) end function TrikeClockwise( speed ) nxt.OutputSetSpeed(2,32,-speed) nxt.OutputSetSpeed(3,32, speed) end function TrikeCounterClockwise( speed ) nxt.OutputSetSpeed(2,32, speed) nxt.OutputSetSpeed(3,32,-speed) end function TrikeStop() nxt.OutputSetSpeed(2,0,0) nxt.OutputSetSpeed(3,0,0) end
Hmmm, there seems to be an awful lot of repeated motor commands. Maybe we can reduce the clutter a bit and make it flexible at the same time by reorganizing the code like this:
-- Make is so we only have to change one function if the motor port changes function MoveTrike( leftSpeed, rightSpeed ) nxt.OutputSetSpeed(2,32,rightSpeed) nxt.OutputSetSpeed(3,32,leftSpeed) end function TrikeForward( speed ) MoveTrike( speed, speed ) end function TrikeReverse( speed ) MoveTrike(-speed,-speed ) end function TrikeClockwise( speed ) MoveTrike(-speed, speed ) end function TrikeCounterClockwise( speed ) MoveTrike( speed,-speed ) end function TrikeStop() MoveTrike( 0, 0 ) end
That looks better! Now we can put it all together in a little test function that moves the Trike in small one second bursts through each of the 5 steps…
For this final step, we’ll still keep the framework of the keypress, but we’ll add a 1 second delay function for each possible motion. That’s long enough to move the Trike, but short enough so that it doesn’t go too far. The Trike should go through each of the steps from the previous section…
-- New test function, use the framework from example 3 function TimedMoves( ) local ticks = nxt.TimerRead() TrikeForward( 80 ) repeat -- this space intentionally left blank until( ticks+1000 < nxt.TimerRead() ) TrikeReverse( 80 ) repeat -- this space intentionally left blank until( ticks+2000 < nxt.TimerRead() ) TrikeClockwise( 80 ) repeat -- this space intentionally left blank until( ticks+3000 < nxt.TimerRead() ) TrikeCounterClockwise( 80 ) repeat -- this space intentionally left blank until( ticks+4000 < nxt.TimerRead() ) TrikeStop( 80 ) end -- Running the test TestTrike( TimedMoves )
That’s it! If you’ve made it this far, you’re ready to add some real robotic functions to your HiTechnic Trike.