The following functions have had API changes!
The NXT API for pbLua follows the C function definitions for the driver layer of the LEGO Firmware Source Code for the NXT, but with a Lua twist. This means that wherever possible or practical, the API accepts optional parameters, and if you don't supply them, you get reasonable defaults.
Note that these APIs may change over time. This is not to annoy you or to make life difficult. It's because writing a decent API is hard to get right, and as I get more feedback I'll update the API if necessary.
With access to these APIs, it is possible to write add-ons for the NXT that are as simple or as complicated as they need to be. You can hack together a little program and test it right away, then improve and expand on it to make more sophisticated applications.
The authors of the LEGO firmware went to great lengths to separate their code into functional modules, and each module gets its own set of API functions in pbLua.
Most functions simply call the corresponding function
in the NXT driver. I've added explanatory notes where
there might be a subtle trick to the operation of the code.
If a function takes parameters, I'll put the values and
what they mean in a table. Many functions have a little
example of how to use them.
The General API is for functions that don't fit neatly into a category
big enough to make a separate section. It includes functions to control
the NXT power, read the buttons and timers, and other miscellaneous
operations.
The NXT has 4 front panel buttons. Each button returns a unique state, and
the result of pressing more than one button at a time is undefined. The state
returned by each button is:
General API
state = nxt.ButtonRead( )
[Top]
| Hex | Decimal | Description |
| 0x0 | 0 | No Button |
| 0x1 | 1 | Grey Button |
| 0x2 | 2 | Right Button |
| 0x4 | 4 | Left Button |
| 0x8 | 8 | Orange Button |
| msec = nxt.TimerRead( ) | [Top] |
Returns the current millisecond timer value. You can read this value at any time. It is very useful for accurate time measurements. There is no way to reset the timer.
| mV, recharge, cap = nxt.BatteryRead( ) | [Top] |
Returns three values related to the battery on the NXT. The first value is the current battery voltage in millivolts. The second value is 0 for a standard battery, and non-zero for a rechargeable battery. The third return value is the capacity of the battery, on a scale from 0 (low) to 4 (lots).
To make things really easy, I used the same values as in the LEGO firmware. The standard battery capacity vs voltage table looks like this:
| Hex | Decimal | Capacity |
| <0x17d4 | <6100 | 0 - very low |
| <0x1964 | <6500 | 1 |
| <0x1B58 | <7000 | 2 |
| <0x1D4C | <7500 | 3 |
| >0x1D4C | >7500 | 4 - high |
The rechargeable battery capacity vs voltage table looks like this:
| Hex | Decimal | Capacity |
| <0x1BBC | <7100 | 0 - very low |
| <0x1C20 | <7200 | 1 |
| <0x1C84 | <7300 | 2 |
| <0x1D4C | <7500 | 3 |
| >0x1D4C | >7500 | 4 - high |
| nxt.EncodeIR( data, mode ) | [Top] |
Takes the three least significant nibbles of data and converts them into a string suitable for sending directly to the HiTechnic IRLink Sensor.
The function automatically calculates the 4th nibble, which is the checksum. The mode parameter works as follows:
| Hex | Decimal | Description |
| 0x0 | 0 | 2400 BPS UART for RCX Comms |
| 0x1 | 1 | IR Trains |
| 0x2 | 2 | IR PowerFunctions |
In most cases, you'll be using the NXT to control PF motors, so use mode 2. To find out more about the IRLink data stream and how each of the specific bits affects the operation, refer to the detailed information on the IR Link provided by LEGO.
| nxt.Checksum( string ) | [Top] |
Calculates the total sum of each byte in string and returns the total which is an unsigned 32 bit value.
| nxt.PowerDown( [x] ) | [Top] |
Depending on the value of x this function affects how the NXT is powered down. Normally, the brick powers down after 30 minutes of inactivity and no console input. If the brick is running a program, then the inactivity timer is disabled, and it is restarted after the program ends.
Note that every time the NXT powers up, the pbLua interpreter is reinitialized. Future versions may change this behaviour and retain the state.
| nxt.Reboot( x ) | [Top] |
Unconditionally reboots the NXT. It is not necessary to press the orange button to turn the NXT on again. Note that you lose whatever you were working on and the entire machine starts from scratch.
| nxt.Reflash( ) | [Top] |
Unconditionally puts the NXT into a state where it can be flashed with new firmware. This avoids having to poke the reset button hidden under the USB port for more than 10 seconds to force the reset.
Note that this function may be removed in future versions if it is abused :-)
| TotalEntries, UsedEntries, FreeEntries, TotalBlocks, UsedBlocks, FreeBlocks = nxt.HeapInfo( ) | [Top] |
Returns a number of useful values for the managed memory heap used by pbLua. The values will change significantly as pbLua allocates and frees memory all the time as you compile and run programs. Here's what the values mean:
This information can be used to determine how badly fragmented the heap is. The ratio of free entries to used entries will tend to go up as the heap gets fragmented, which means that there are a lot of unused entries on the heap. The same goes for free blocks.
| UserFlashStart, FlashStart, FlashEnd, RamStart, RamEnd = nxt.MemInfo( ) | [Top] |
Returns a number of useful values for system memory spaces, the most useful being UserFlashStart. The typical values as of Beta 13o are:
| Name | Hex | Decimal |
| UserFlashStart | 0x00128C00 | 1215488 |
| FlashStart | 0x00100000 | 1048576 |
| FlashEnd | 0x00140000 | 1310720 |
| RamStart | 0x00200000 | 2097152 |
| RamEnd | 0x00210000 | 2162688 |
| s = nxt.MemRead( addr, length ) | [Top] |
Returns the length bytes starting at addr as a string of bytes. The string can have embedded NULLs and can be manipulated by the Lua string library.
The addr can be in either RAM space or in FLASH, there is no difference from the user's point of view.
| n = nxt.MemWrite( addr, string ) | [Top] |
Writes the string of bytes at addr. Returns 1 if the write was in RAM space, 2 if the write was in FLASH, and 0 if the write is outside the valid memory space.
When writing to FLASH, the algorithm used is smart enough to figure out if any bits are changing from 0 to 1, which requires an erase before the actual write.
Furthermore, the write is smart enough to only touch the bytes that are actually being written. You don't have to save the rest of the page first!
Because of the previous two points, we can easily fold the erase function into the write function. Simply make a string of 256 FF bytes and write it to the FLASH you want to erase, but it's probably easier to simply do the write and let the routine take care of doing the erase for you.
The addr can be in either RAM space or in FLASH, there is no difference from the user's point of view.
The Integer Math API adds a few simple routines to the exising math capabilities of the Lua core. They are distinct from the Float Math API because the operate on and return normal Lua integers.
| a = nxt.random( [range=0], [offset=0] ) | [Top] |
Returns a random 32 bit value with range and offset depending on the values as described below:
If offset is non-zero, it is added to the resulting random number. This makes it possible to return random numbers in pretty much any range that would be useful for a NXT robot.
| a = nxt.min( x, y ) | [Top] |
| a = nxt.max( x, y ) | [Top] |
| a = nxt.sign( x ) | [Top] |
Returns the sign of a (an integer) as an integer.
Returns a = 0 if x = 0
Returns a = -1 if x < 0
| a = nxt.abs( b ) | [Top] |
Returns the absolute value of the integer b.
The Logical Math API adds the basic logical operators that work on Lua integers. Note that these are different than the standard Lua logical operators that return boolean types!
In pbLua, integers are 32 bit values, which gives us plenty of bits for most uses. Using arbitrary strings for bitwise operands looks attractive, until you realize that strings are immutable objects, which means that changing them involves a comlete copy befor the changes are applied. This can lead to unexpected slowness.
| a = nxt.bnot( b ) | [Top] |
Returns the bitwise inversion of all the bits in b. In C, you would write:
| a = nxt.band( b, c ) | [Top] |
Returns the bitwise and of b and c. In C, you would write:
| a = nxt.bor( b, c ) | [Top] |
Returns the bitwise or of b and c. In C, you would write:
| a = nxt.bxor( b, c ) | [Top] |
Returns the bitwise exclusive or of b and c. In C, you would write:
| a = nxt.blshift( b, c ) | [Top] |
Returns the left shift of b as an unsigned value by c bits.
| a = nxt.brshift( b, c ) | [Top] |
Returns the right shift of b as an unsigned value by c bits.
a = (b >> c);
The Math API is a compromise between the high speed that we get from the all-integer implementation of the Lua core and the need for more complex calculations that involve real world signals. It is based on the well known IEEE 754 Single Precision Float model, which is normally represented in C as the float type. The precision and range is not very good compared to double but it's certainly good enough for anything you're likely to use for NXT robots.
For example, we may want to calculate the distance components (x,y) given a reading from an ultrasonic sensor pointed 37 degrees to the left of the robot. This will involve sin() and cos() calculations, which we can do using the Math API.
For a complete discussion on how to use floating point numbers, refer to the Floating Point Math tutorial
The trigonometric functions normally use radians as the angular measurement, but this is probably not intuitive for most users. Instead, pbLua used degrees as the units for measuring angles.
| a = nxt.sin( x ) | [Top] |
| a = nxt.cos( x ) | [Top] |
| a = nxt.tan( x ) | [Top] |
| a = nxt.asin( x ) | [Top] |
| a = nxt.acos( x ) | [Top] |
| a = nxt.atan( x ) | [Top] |
| a = nxt.atan2( x, y ) | [Top] |
| a = nxt.pi( ) | [Top] |
Returns the constant pi.
The logarithmic functions include operators to do powers, natural and base 10 logarithms, and the square root.
| a = nxt.exp( x ) | [Top] |
| a = nxt.exp( x, y ) | [Top] |
| a = nxt.log( x ) | [Top] |
| a = nxt.log10( x ) | [Top] |
| a = nxt.sqrt( x ) | [Top] |
Returns a = sqrt(x)
Here's where we put functions that don't really fit anywhere else
| a = nxt.ceil( x ) | [Top] |
Returns a = ceil(x), the integer (as float) larger than or equal to x
| a = nxt.floor( x ) | [Top] |
Returns a = floor(x), the integer (as float) smaller than or equal to x
| a = nxt.sign( x ) | [Top] |
Returns the sign of a (an integer) as an integer.
Returns a = 0 if x = 0
Returns a = -1 if x < 0
| a = nxt.abs( b ) | [Top] |
Returns the absolute value of the integer b.
| a = nxt.min( x, y ) | [Top] |
| a = nxt.max( x, y ) | [Top] |
Returns a = max( x, y )
The NXT display is an LCD with 64 rows of 100 pixels. The top left corner has coordinates (0,0) and the bottom right corner is (99,63). The NXT refreshes the display automatically about 6 times every second, so it is not necessary to call a routine to actually update the dispay.
The current text display capabilities are very basic at this point. There is only one font, and each character is 8 pixels high by 6 pixels wide. The display can show 8 rows of about 16 characters. You can scroll the entire display up by one line (8 pixels), and writing text simply puts the new text string on the bottom row of the display.
The display functions are minimal right now. If there is a pressing need for more sophisticated routines such as drawing lines or circles I could certainly do that. I've also received a much smaller 4x6 font and plan to use it with code that lets you put text at an arbitrary location on the screen. I may also add a call to disable display updates if necessary.
| nxt.DisplayClear( ) | [Top] |
Turns all the pixels in the display off.
| state = nxt.DisplayFlip( [state] ) | [Top] |
Flips the display updside-down so you can read it the other way around. The state parameter works as follows:
The function returns the new state of the display orientation.
| state = nxt.DisplayInvert( [state] ) | [Top] |
changes all dark pixels to light and vice versa. Useful for "flashing" the display to tell the user something important has happened. The state parameter works as follows:
The function returns the new state of the display pixels.
| nxt.DisplayText( string [, x, y, inv] ) | [Top] |
Prints the string in the display with the top left corner of the text box starting at (x, y). If x is omitted, the text starts on the left side of the display. If y is omitted the text starts on the bottom row of the display.
The function also accepts an inv parameter that is normally 0 for black text on a white background. A non-zero value makes the text white on a black background.
Only the pixels for the characters that fit on a line are written to the display.
| nxt.DisplayScroll( ) | [Top] |
Scrolls the entire display up by one line or 8 pixels.
Future versions of this routine might have an optional parameter for the number of pixels to scroll, with the default being 8.
| nxt.DisplayPixel( x, y, [state=1] ) | [Top] |
Sets or clears the pixel given by the display coordinate pair (x,y) depending on the value of state. The state is an optional parameter that defaults to 1 which turns the pixel on. To turn the pixel off, just pass a 0 for state.
| pixel = nxt.DisplayGetPixel( x, y ) | [Top] |
Returns 1 if the pixel given by the display coordinate pair (x,y) is turned on, and 0 if the pixel is turned off. This feature was requested by a pbLua user that was not sure what to use it for, but screenshots come to mind...
The sound API allows you to play arbitrary tones within the limits of the NXT sound drivers. The range of frequencies is from 220 Hz to 14080 Hz, and the minimum tone duration is 10 msec.
The volume is specified as an integer from 0 (silence) to 4 (max volume).
| state = nxt.SoundGetStatus( ) | [Top] |
Returns the state of the sound system. A value of 0 means that a sound or melody is being played, while a non-0 value means that the system is ready for a new tone or melody.
| nxt.SoundTone( [frequency=1000], [duration=10], [volume=1] ) | [Top] |
Starts playing a tone at frequency for duration msec at volume.
All the parameters are optional, and you get a reasonable default if you specify none of them.
| nxt.SoundMelody( melody, [volume=1] ) | [Top] |
Starts playing a series of tones specified in the melody string at volume.
The melody string is a series of 16 bit value pairs in big-endian format. The first value in the pair is the frequency in Hz, the second value is the duration of the tone in msec.
For example, to specify a simple melody of 1024 Hz for 512 msec followed by 2048 hz for 128 msec the following bytes are needed: 0x04, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x08
The Input Port control functions deal with the voltage delivered to the device connected to the output port, as well as the state and direction of of the digital control pins. There is a separate I2C that sends I2C messages to intelligent sensors connected to the input ports.
While it's a nice idea to have dedicated functions for each sensor type, this is contrary to the philosopy of the API. You use the API to design your own sensor procesing functions, and it's really not that hard to do.
See the I2C API for some examples that use these sensors.
| nxt.InputSetType( port, [type=0] ) | [Top] |
| Hex | Decimal | Description |
| 0x00 | 0 | Inactive - touch sensor |
| 0x01 | 1 | Active - sound and light sensor |
| 0x02 | 2 | 9V - ultrasonic sensor |
| nxt.InputSetDir( port, dir0, dir1 ) | [Top] |
Sets the direction of the digi0 and digi0 pins on the specified port. The direction values must be specified as either 0, which makes the pin an input, or non-zero which makes the pin an output.
| nxt.InputSetState( port, state0, state1 ) | [Top] |
Sets the state of the digi0 and digi0 pins on the specified port. The state values must be specified as either 0, which sets the pin high, or non-zero which sets the pin to low. Note: the state is only changed if the pin is configured to be an output.
| rawAD, state0, state1 = nxt.InputGetStatus( port ) | [Top] |
Gets the raw A/D value and the digi0 and digi0 pin state of the specified port. The raw A/D value depends on the sensor type and may need to be processed to give a useful value for your application.
The Low Speed Digital Communication functions allow the input ports on the NXT to communicate with smart sensors over an I2C bus. This is the port that some of the third party vendors will use for add-ons such as colour and compass sensors, as well as motor and additional input multiplexors.
See the sensortest.txt file in the pbLua distribution for an example of how the low speed port is used.
pbLua allows you to format and save strings in variables, so they can be used over again, even for different ports. pbLua also has built in coroutines, which are a very powerful way to do cooperative multitasking. This will be explained in more detail in a separate page.
| nxt.I2CInitPins( port ) | [Top] |
Initializes the port pin state for I2C communication. This function must be called before any communication with I2C devices on a specific port. It is not necessary to call this function before every message though.
| nxt.I2CSendData( port, string, rxlen ) | [Top] |
Sends the string to the specified port, and sets up the receiver to get rxlen bytes. Both string being sent and rxlen have a maximum value of 16 bytes.
The I2CSendData() function is non-blocking, which means you can do other things besides wait in a busy loop for the communication with the I2C device to complete.
Note that the previous version of this function required the txlen parameter to be specified, but the newer version determines this automatically from the string parameter.
| string = nxt.I2CReceiveData( port, rxlen ) | [Top] |
This function returns a string that is rxlen bytes long from the specified port. It is important that both the transmit and recieve status indicate completion before reading the data from the I2C port. It is also OK to check that the I2C channel is idle.
The string may have embedded 0x00 bytes, it can be turned into individual bytes using pbLuas string functions. See the example pages for more detail.
| state, txstatus, rxstatus = nxt.I2CGetStatus( port ) | [Top] |
Returns the overall I2C driver status as well as the txstatus and rxstatus of the specified port. Previous versions returned only the rx and tx status which may not be enough information. The overall I2C driver state can have the following values:
| Hex | Decimal | Description |
| 0x00 | 0 | LOWSPEED_IDLE |
| 0x01 | 1 | LOWSPEED_TX_STOP_BIT |
| 0x02 | 2 | LOWSPEED_TRANSMITTING |
| 0x04 | 4 | LOWSPEED_RECEIVING |
| 0x08 | 8 | LOWSPEED_TEST_WAIT_STATE |
| 0x10 | 16 | LOWSPEED_RESTART_CONDITION |
| 0x20 | 32 | LOWSPEED_WAIT_BEFORE_RX |
| Hex | Decimal | Description |
| 0x00 | 0 | Busy |
| 0x01 | 1 | Complete |
| 0xFF | 255 | Timeout |
The High Speed Digital Communication functions only work on Port 4. It is designed to work as a standard input port as well as a half-duplex RS485 serial port. If you don't know what this means, then don't monkey around with it.
You can use this port to connect many NXTs using a custom serial protocol.
| nxt.RS485Enable( rate ) | [Top] |
Initializes the 485 interface for communication. A value of 0 turns the interface off, and a non-zero value initializes it for communication.
The defaults are 921,600 bits per second, 8 data bits, no parity, 1 stop bit.
The allowable values for the baud rate are:
If you specify a non-standard baud rate, the rate defaults to 921,600 bits per second.
| nxt.RS485SendData( string ) | [Top] |
Sends the bytes of string to the high speed port. The function sends a maximum of 128 bytes at a time.
Note that there is currently no way to check and see if the interface is ready to accept more data. The original LEGO firmware only calls this routine once every 1 msec, and the interface sends about 100 characters per msec, so there's not much chance of overflow.
Future versions of this routine may have to report if the data could be sent.
| string = nxt.RS485RecvData( ) | [Top] |
This function returns the characters available in the RS485 receive buffer as a string.
The string may have embedded 0x00 bytes, it can be turned into individual bytes using pbLuas string functions. See the example pages for more detail.
The Output API is used to control the 3 output ports on the NXT. The LEGO firmware includes some very sophisticated motor control algorithms for these ports, and pbLua hooks right into all of them.
You can use the basic Output API to simply turn specific motors on or off, or you can use the functions that include tachometer control to make the motor turn to a specific position. Finally, you can write your own custom motor control functions when you need very special characteristics for motor operation.
See the sample program pages for examples of how to use the motor functions effectively.
The motor control API functions all take the motor number as the first parameter. Use the table provided to translate between the motor letter engraved in the NXT over the output ports, and the motor number the API functions need.
| Hex | Decimal | Description |
| 0x1 | 1 | Motor Port A |
| 0x2 | 2 | Motor Port B |
| 0x3 | 3 | Motor Port C |
| nxt.OutputSetRegulation( motor, [state=0], [mode=1] ) | [Top] |
Sets the regulation state and mode for the specified motor.
The state parameter affects the type of speed regulation performed by the firmware as described in the table below:
| Hex | Decimal | Description |
| 0x0 | 0 | Idle - default, no speed regulation |
| 0x1 | 1 | Regulated - single motor speed regulation |
| 0x2 | 2 | Synchronized - motor pair speed regualtion |
The mode parameter affects the operation of the motor driver chip as described in the table below:
| Hex | Decimal | Description |
| 0x0 | 0 | Float |
| 0x1 | 1 | Brake - default |
| speed, tacho, blocktacho, runstate, overload, rotcount, torun, forever = nxt.OutputGetStatus( motor ) | [Top] |
This function returns a number of values related to the status of the specified motor. It is not necessary to specify all of the return values when calling this function as pbLua automatically throws away values you don't use.
These values are copied from the internal values in the driver once every millisecond. Some of the values such as runstate and overloaded are only updated once every regulation period, which is 10 milliseconds in the pbLua firmware.
-- Examples for nxt.OutputGetStatus() -- Read the current speed that the motor is trying to acheive speed = nxt.OutputGetStatus(1) -- Read the current speed and tachometer value speed,tacho = nxt.OutputGetStatus(1) -- Read just the current runstate of the motor _,_,_,runstate = nxt.OutputGetStatus(1) -- The underscore "_" is just a Lua idiom for a variable that we don't care -- about and will throw away...
The speed value is the current speed that the motor is trying to get to, which includes the effect of speed regulation. Under normal operating conditions, if you set the speed of the motor to 50, then speed will hover around 50 as well. If you slow the motor down by adding more load, the motor will have to "catch up" to keep the speed setting. By continuously displaying the speed, you'll see what I'm talking about.
The tacho value is the current accumulated tachometer reading of the motor. There are 360 counts in a full revolution. The tacho value is signed, so as the motor turns forwards the count increases, and as the motor turns backwards, the count decreases, eventually passing through 0 and becoming negative. It is possible to reset the tacho value to zero using the nxt.OutputResetTacho() function.
The blocktacho value is the current incremental tachometer reading of the motor. In the standard NXT firmware, this is reset before every motor control block, but you can use it as an auxilliary tachometer reading so that you don't always have to keep calculating a difference from a saved tacho value. It is possible to reset the rotcount value to zero using the nxt.OutputResetTacho() function.
The runstate value is the current running state of the specified motor. Refer to the nxt.OutputSetSpeed() function for more details.
The overloaded value is the current overload state of the specified motor. The overloaded value is normally zero, but when running in regulated mode, the driver may determine that the motor must be driven past maximum power to keep up with the desired speed, which then sets the overloaded flag.
The rotcount value is another current incremental tachometer reading of the motor. This one is separate from the blocktacho value used by the standard NXT firmware. Think of it as additional incremental counter you can use. It is possible to reset the rotcount value to zero using the nxt.OutputResetTacho() function.
The torun value is the target tacho value. This is most often useful when you want to figure out how far away the motor is from the desired setpoint. Use this when you are checking to see if the motor has finished what you asked it to do in a previous command. The value is undefined if you're not running the motor for a specific number of tacho counts.
| nxt.OutputResetTacho( motor, [tacho=0], [block=0], [rot=0] ) | [Top] |
Resets the tacho counters for the specified motors. The default values for tacho, block, and rot are all 0, which does nothing to the specific tacho counter.
A non-zero value for the parameters will reset the specified tacho counter as follows:
See nxt.OutputGetMotorStatus() for a detailed decription of the tacho values.
| nxt.OutputSetPID( motor, P, I, D ) | [Top] |
Sets the P, I, and D of the specified motor when running in regulated mode. These are beyond the scope of this API document and are left as a topic for a separate tutorial.
In general you don't need to mess with this - so don't.
| nxt.OutputSetSpeed( motor, [runstate=0], [speed=0], [tacho=0], [turn=0] ) | [Top] |
Sets the runstate, speed, and turn-param of the specified motor.
The runstate parameter basically controls the type of motion the motor is going to do, some of which are only really useful when the motor is in regulated operation.
| Hex | Decimal | Description |
| 0x00 | 0 | Idle Motor - default |
| 0x10 | 16 | Ramp Up Motor |
| 0x20 | 32 | Run Motor |
| 0x40 | 64 | Ramp Down Motor |
| 0x60 | 96 | Hold Motor |
The Ramp Up and Ramp Down operation depends on what your desired speed setting is and how far (how many tacho counts) the ramp duration is. It's beyond the scope of this API document and is left for a separate tutorial.
The speed parameter controls the rotation speed of the motor. Positive numbers are forwards, negative numbers are reverse. The speed should not be set to an absolute value larger than 100 or smaller than 10.
Note that there is currently no check for this in pbLua - it needs to be added.
The tacho parameter specifies how many counts the motor should turn in the direction specified by speed. Do not specify a negative number of counts, use a negative speed instead.
The turn parameter controls the relative rotation speed of two motors. It's beyond the scope of this API document and is left for a separate tutorial.
The NXT Bluetooth API is still being worked out as we get more familiar with how the Bluetooth operates over multiple channels. Check back for more details, but here is a smaple of the calls that are currently available - use at your own risk.
These functions are really not meant to be used as stand-alone comments. Instead, they should be considered as the building blocks of a communication toolkit they yo assemble to meet your needs. The BlueCore card inside the NXT is "smart" and it remembers a lot of useful things like the name of your NXT, devices it has seen and connected to and their PINs, and so on.
See the sample program pages for examples of how to use the Bluetooth functions effectively.
Note that the Bluetooth functionality is still a pretty big block of stuff that I need to test and document. I'd be grateful if you make any discoveries and can feed them back to me...
| nxt.BtFactoryReset( ) | [Top] |
Restores the BlueCore board to its factory fresh state, which only clears saved connection and device specific information. It does not erase the BlueCore firmware.
| nxt.BtPower( [state=1] ) | [Top] |
Turns the Bluetooth system on the NXT on or off depending on state. The default is to turn Bluetooth on if you don't give a value for state or if it's non-zero. A value of 0 for state turns the Bluetooth system off, which is useful if you want to save your batteries.
| state, active, update = nxt.BtGetStatus( ) | [Top] |
Returns the state, active, and update values that help to decode the state of the Bluetooth subsystem in the NXT as summarized in the tables below.
The state value is the value you will use the most. It provides a good overview of the major status information that you'll need to use the Bluetooth capability in the NXT. The individual bit positions and their meaning follows:
| Hex | Decimal | Description |
| 0x01 | 1 | BT_STATE_VISIBLE - visible |
| 0x02 | 2 | BT_STATE_CONNECTED - connected to something |
| 0x04 | 4 | BT_STATE_OFF - power off |
| 0x08 | 8 | BT_ERROR_ATTENTION - error attention |
| 0x40 | 64 | BT_CONNECT_REQUEST - get connect accept in progress |
| 0x80 | 128 | BT_PIN_REQUEST - get pin code |
The active value tells you what state the Bluetooth handler is in. The state is updated once every millisecond, and may change depending on what's going on. Not all states are useful to the pbLua API, and for a detailed understanding of what's going on, you'll need to read the source code of the LEGO driver.
That being said, the active value is probably quite useful for debugging...
| Hex | Decimal | Description |
| 0x00 | 0 | UPD_BRICKNAME |
| 0x01 | 1 | UPD_FACTORYRESET |
| 0x02 | 2 | UPD_OPENSTREAM |
| 0x03 | 3 | UPD_REQCMDMODE |
| 0x04 | 4 | UPD_CONNECT |
| 0x05 | 5 | UPD_CONNECTREQ |
| 0x06 | 6 | UPD_PINREQ |
| 0x07 | 7 | UPD_DISCONNECT |
| 0x08 | 8 | UPD_DISCONNECTALL |
| 0x09 | 9 | UPD_REMOVEDEVICE |
| 0x0A | 10 | UPD_SEARCH |
| 0x0B | 11 | UPD_RESET |
| 0x0C | 12 | UPD_EXTREAD |
| 0x0D | 13 | UPD_SENDFILE |
| 0x0E | 14 | UPD_OFF |
| 0x0F | 15 | UPD_VISIBILITY |
| 0x10 | 16 | UPD_SENDDATA |
| 0x11 | 17 | UPD_IDLE |
The update value is even more esoteric - it describes the substate within the active state of the Bluetooth state machine. If you need this value for debugging, then you're going to be reading the Bluetooth driver update code from LEGO. Note that there are actually more return values, but they may be removed later - I just used them for debugging things.
| nxt.BtVisible( [state=1] ) | [Top] |
Turns the visibility of the Bluetooth system on the NXT on or off depending on state. The default is to make the NXT visible if you don't give a value for state or if it's non-zero. A value of 0 for state makes the NXT invisible to other Bluetooth devices.
| nxt.BtSearch( [state=1] ) | [Top] |
Turns the search mode of the Bluetooth system on the NXT on or off depending on state. The default is to make the NXT search for other devices if you don't give a value for state or if it's non-zero. A value of 0 for state makes the NXT stop searching for other Bluetooth devices.
There is a timeout if no new Bluetooth device has been found, and the search mode is automatically aborted.
| name, class, addr, status = nxt.BtGetDeviceEntry( idx ) | [Top] |
Returns the name, class, addr, and status values of the device specified by idx. Up to 30 devices can be tracked by the BlueCore card in the NXT.
The name is returned as a string of SIZE_OF_BT_NAME (16) characters that is padded out with NULLs if necessary.
The class is returned as an integer representing the power of device that has been found by the BlueCore according to the following table:
| Hex | Decimal | Description |
| 0x1 | 1 | 100mW - Range ~100m | FIXME:
The addr is returned as a string of SIZE_OF_BDADDR (7) characters that is padded out with NULLs if necessary. It represents the MAC address of the Bluetooth device.
The status is returned as an integer with bits set according to the table below:
| Hex | Decimal | Description |
| 0x00 | 0 | BT_DEVICE_EMPTY |
| 0x01 | 1 | BT_DEVICE_UNKNOWN |
| 0x02 | 2 | BT_DEVICE_KNOWN |
| 0x40 | 64 | BT_DEVICE_NAME |
| 0x80 | 128 | BT_DEVICE_AWAY |
| nxt.BtSetName( name ) | [Top] |
Sets the name of the NXT to the first SIZE_OF_BT_NAME-1 (15) characters of name. If name is less than 15 characters the remaining bytes are filles with NULLs.
| nxt.BtSetPIN( pin ) | [Top] |
Sends the PIN code for the device requesting a connection as the first SIZE_OF_BT_PINCODE (16) characters of pin.
Note that this function won't do anything unless the Bluetooth system state has the BT_CONNECT_REQUEST and BT_PIN_REQUEST bits set.
| name, class, pin, addr, handle, status, linkq = nxt.BtGetConnectEntry( idx ) | [Top] |
Returns the name, class, pin, addr, handle, status, and linkq values of the connection specified by idx. Up to 4 connections can be tracked by the BlueCore card in the NXT. The first connection (idx=0) is always reserved for incoming connections. The other three connections (idx=1,2,3) are for outgoing connections. Only one connection can be active at a time??
The name is returned as a string of SIZE_OF_BT_NAME (16) characters that is padded out with NULLs if necessary.
The class is returned as an integer representing the power of device that has been found by the BlueCore according to the following table:
| Hex | Decimal | Description |
| 0x1 | 1 | 100mW - Range ~100m |
| 0x2 | 2 | 2.5mW - Range ~10m |
| 0x3 | 3 | 1mW - Range ~1m |
The pin is returned as a string of SIZE_OF_BT_PINCODE (16) characters that is padded out with NULLs if necessary.
The addr is returned as a string of SIZE_OF_BDADDR (7) characters that is padded out with NULLs if necessary. It represents the MAC address of the Bluetooth device.
The handle is returned as an integer. I'm not sure exactly how the handle works, but I do know that BLUETOOTH_HANDLE_UNDEFIEND is 255 (0xFF).
The status is returned as an integer that represents the streaming status of the connection. A non-zero value (typically 1) means the connection is ready to stream data.
The linkq is returned as an integer that is supposed to represent the link quality of the connection. As far as I can tell, this is always 0.
| nxt.BtConnect( con, dev ) | [Top] |
Attempts to connect the device indexed by dev to the connection indexed by con. The first connection (idx=0) is always reserved for incoming connections. The other three connections (idx=1,2,3) are for outgoing connections.
This statement only begins the connection process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and act accordingly.
Eventually we'll have example code for this...
| nxt.BtDisconnect( con ) | [Top] |
Disconnects the Bluetooth connection indexed by con.
| nxt.BtDisconnectAll( ) | [Top] |
Disconnects all the BLuetooth connections.
| nxt.BtStreamMode( [ignore=0] ) | [Top] |
Sets the stream mode for subsequent transmissions depending on the value of ignore which has a default value of 0 if not specified.
When ignore is set to 0, the firmware inserts a stream length at the beginning of a transmission. Similarly, when receiving stream data, the length is expected at the beginning.
When ignore is set to a non-0 value, the firmware does not insert a stream length at the beginning of a transmission. Similarly, when receiving stream data, the length is not expected at the beginning.
| nxt.BtStreamSend( con, string, [reply=0], [timeout=30000] ) | [Top] |
Sends the string to the selected con. Note that the string is sent as raw bytes, and it is permitted to send arbitrary data, even NULLs. Also keep in mind if your target device is expecting length prefix bytes or not, and set the stream mode accordingly.
The optional reply length tells the firmware how many bytes are to be accepted, and the timeout tells the firmware how long to wait for them.
I'm not too sure how this all works, so any information that you want to send my way would be helpful.
| nxt.BtStreamRecv( ) | [Top] |
Returns the bytes received to this point by the Bluetooth subsystem as a string. If there are no bytes ready, then returns NIL.
The File API is an important addition ot the pbLua system because it finally gives programmers a way to store their files in non-volatile memory, it allows the creation of logs for data collection, and even lets the NXT execute files automatically on power up!
The main difficulty that experienced programmers will have with the File API is that it does not work exactly like the stdio.h functions.
You cannot just open a file for read/write access and have the system look after adding to it willy nilly. You have to tell it the maximum size you expect to write, but if you write less then that's all the space you use.
When you write and read data from the files, the stings can have embedded NULLs and be just about any length within the limits of the RAM at the time the code is running.
This is mainly because the API has been designed to work with minimum support code requirements in limited FLASH space. Just remember that you're working on a highly embedded platform and that sometimes tradeoffs have to be made.
The nice thing about the Lua core is that there is good support for error handling and messages, so if something does go wrong with your file functions, at least you'll get useful error messages. The following table summarizes the error codes that the pbLua File API can show:
| Symbolic | Numeric | Description |
| ENOENT | 2 | No such file or directory |
| EBADF | 9 | Bad file number |
| EACCES | 13 | Permission denied |
| EBUSY | 16 | Resource busy |
| EEXIST | 17 | File exists |
| ENFILE | 23 | File table overflow |
| ENOSPC | 28 | No space left on device |
| ESPIPE | 29 | Illegal seek |
| ENAMETOOLONG | 36 | File name too long |
File names are a maximum of 12 characters long, and they can be any characters you want. So if you want a file called "#$%@&^%", then go ahead, but why would you really want to do that? There are some examples of using the File API in the standard distribution.
There are a maximum of 31 files available in the file descriptor block. That's a practical compromise between a small and large number of files. To minimize the number of writes to the file descriptor block, erased files are not actually erased, their entries are simpy zeroed out and the handle is NOT available for use right away.
When you run out of file handles, a simple call to nxt.FileFormat(0) in the file API will copy the file descriptor block to a new location and make the erased file handles usable again. Your existing files are not affected.
| b = nxt.FileExists( name ) | [Top] |
Checks if the file given by name exists and returns a boolean true if the file exists and false if it does not. Note that even if the file exists, it may be open for writing or reading already.
Use this function if you need to be sure that a file exists (or doesn't) before running code that can throw an error and stop your application.
-- Check if a file exists
if nxt.FileExists( "testFile" ) then
print( "It exists" )
else
print( "It does not exists" )
end
| handle = nxt.FileOpen( name ) | [Top] |
Opens the file given by name. The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
If the call succeeds, then a file handle is returned. The handle is actually the index in the file descriptor table. You need to use the file handle returned by this function for any other file operations like reading, writing, or closing the file.
-- Open a file for reading h = nxt.FileOpen( "testFile" )
| handle = nxt.FileCreate( name, maxbytes ) | [Top] |
Opens the file given by name and allocates maxbytes of space for the file. The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
If the call succeeds, then a file handle is returned. The handle is actually the index in the file descriptor table. You need to use the file handle returned by this function for any other file operations like reading, writing, or closing the file.
-- Create a file for writing h = nxt.FileCreate( "testFile", 1024 )
| bytes = nxt.FileWrite( handle, string ) | [Top] |
Writes the characters in string to the file indicated by handle. The handle must be the result of a previous call to FileCreate(). The string is any standard Lua string value and can have embedded NULL (0x00) characters. The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
The number of bytes actually written is returnd, and it's up to you to figure out if any bytes were missed. The FileWrite() function will never write more bytes than were allocated at the time the file was opened.
-- Write a string to a file, h is a handle from a previous FileCreate() b = nxt.FileWrite( h, "Hello World" ) b = nxt.FileWrite( h, string.rep( "a", 80 ) )
It is anticipated that this function will find a lot of use when the NXT is used to log data or events and you want to record this as human readable data.
| string = nxt.FileRead( handle, [bytes | *l |*a | ?a] ) | [Top] |
Returns the string of characters from the file indicated by handle depending on the value of the second parameter. The special file handle "0" (zero) can be used to read data from the console, which is helpful if you want to read data from the user. The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
The following table describes how the second parameter to FileRead controls the ammount of data that is read:
| Code | Description |
| n | Returns up to n bytes from the current file position |
| *l | Returns bytes from the current file position to the next line break character and discards the line break |
| *a | Returns the rest of the file from the current file position. |
| ?a | Returns any characters available in the buffer. This is only valid for file handle 0 - which is the console | )%>
The FileRead() function will never return more bytes than were written at the time the file was created.
If FileRead() is reading the entire file or n bytes from the console and CTRL-Z is entered, then the function stops and returns the bytes read up to but not including the CTRL-Z. If FileRead() is reading a line of input, then either CR or LF will terminate the line.
-- Read 6 bytes from a file, h is a handle from a previous FileOpen() s = nxt.FileRead( h, 6 ) -- Read a line from a file s = nxt.FileRead( h, "*l" ) -- Read the entire file
Note that it is possible to dump the bytecodes that represent a Lua function to a string and save it for later execution, but that's a more advanced topic that I'll leave for the tutorial page on using the File API.
| status = nxt.FileSeek( handle, offset ) | [Top] |
Attempts to set the file pointer of the file indicated by handle to the value given by offset. The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
-- Skip the first 8 bytes in a file nxt.FileSeek( h, 8 )
The return value is always 0 if the function succeeds, but this may change if we add a third parameter to allow us to perform absolute or relative seeks
| status = nxt.FileClose( handle ) | [Top] |
Attempts to close the file descriptor of the file indicated by handle The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
If the file was originally opened for writing then the current value of the file pointer is written to the file descriptor in FLASH.
The return value is the original file handle if the function succeeds.
-- Close the file, h is a handle from a previous FileOpen() s = nxt.FileClose( h )
| handle = nxt.FileDelete( name ) | [Top] |
Deletes the file given by name. The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
If the call succeeds, then the file's old handle is returned.
Keep in mind that deleting a file frees up space in the FLASH for new files, but the old file handle cannot be used again until the filesystem directory block is moved to a new location. See FileFormat(0) for more information.
-- Delete a file h = nxt.FileDelete( "testFile" )
| type, name, block, maxBytes, curBytes, curPtr = nxt.FileHandleInfo( handle ) | [Top] |
Returns information about the file handles (descriptors) as follows:
| Hex | Decimal | Description |
| 1 | 1 | Free header ready for a new file |
| 2 | 2 | Erased header that cannot be used |
| 3 | 3 | The Master File System Header |
| 4 | 4 | A file header that is open for writing |
| 5 | 5 | A file header that can used for reading |
The call can fail if:
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
| files, blocks, blockSize = nxt.FileSysInfo( handle ) | [Top] |
Returns information about the FLASH file system as follows:
| status = nxt.FileFormat( type ) | [Top] |
Formats the file system according to the type parameter as follows:
| Format | Description |
| 0 | Moves the file descriptor block to another location in the user FLASH area, making deleted file handles usable for new files |
| 1 | Creates a brand new clean file descriptor block and erases all the user files |
In normal use, the most common format type is 0. You use this when you have run out of free file descriptors, which can happen if you have been erasing and creating files. By doing this kind of format, the file system moves the existing file descriptor block to a new location and makes the erased file descriptors available for new information.
The full format option 1 is for when you want to start with a fresh file system and are willing to erase all your files.
| chunk = nxt.loadfile( name ) | [Top] |
Loads the file given by name and returns a Lua "chunk" that can be executed later. Please refer to the Lua User Guide sections on chunks of code and the loadfile() function for more details on how to use this function.
The call can fail if:
The loadfile() function is most useful if you have some code that you want to run over and over again that is stored in a file. Rather than "running" the file, which requires the file to be read, interpreted, and then executed, you can save the result of the read and interpret operations in a chunk that can be executed later.
-- Lets say that a file called "hello" has this content: -- print( "Hello World - it's pbLua!" ) -- -- You use the loadfile() function like this: -- -- c = nxt.load( "hello" ) -- -- There is no output on the console because the contents of the file -- do not actually get executed. To run the chunk, type: -- -- c() -- -- And the console prints out: -- -- Hello World - it's pbLua!
| nxt.dofile( name ) | [Top] |
Loads and executes the file given by name. Please refer to the Lua User Guide section on dofile() function for more details on how to use this function.
The call can fail if:
The nxt.dofile() function is most useful if you have some code in a file that runs occasionally, most often some startup code for your application that can itself call nxt.dofile() to run other files. This is commonly called "bootstrapping" your application.
-- Lets say that a file called "hello" has this content: -- print( "Hello World - it's pbLua!" ) -- -- You use the dofile() function like this: -- -- nxt.dofile( "hello" ) -- -- And the console prints out: -- -- Hello World - it's pbLua!
| string|chunk = nxt.FileChooser( [operation], timeout ) | [Top] |
The FileChooser() function is a powerful addition to the File operations available in pbLua. It is the simplest way to run programs on the NXT because it does not require a console device to be conneted to the NXT via USB or Bluetooth.
When the FileChooser() function runs, it brings up a dialog box on the LCD screen of the NXT that allows the user to scroll through the available file handles using the grey arrow keys on the front of the NXT.
If you don't specify a timeout, the default is 15 seconds. You can specify any value (in seconds) for a timeout. The timeout is expended every time one of the grey arrow buttons is pressed. If the timeout expires, this function returns nil.
If you want to exit without choosing a file, then just press the grey rectangle button and the function returns nil.
Pressing the square orange button "selects" the file and performs an operation on it that depends on the operation parameter that was passed to the function:
| Operation | Description |
| name | Returns the name of the file |
| file | Returns entire contents of the file |
| loadfile | Returns the chunk resulting from reading and interpreting the contents of the file |
| dofile | Reads, interprets, and executes the contents of the file |
The call can fail if: