Introduction
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.
- General API
- Bitwise Math API
- Float Math API
- Trigonometric Float Math API
- Logarithmic Float Math API
- Raw Float Math API
- Miscellaneous Math API
- Display API
- Sound API
- Input Port API
- Low Speed I2C API
- High Speed 485 API
- Output API
- Bluetooth API
- File API
- Memory API
General API
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.
state = nxt.ButtonRead( )
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:
| Hex | Decimal | Description |
|---|---|---|
| 0x0 | 0 | No Button Pressed |
| 0x1 | 1 | Grey Button Pressed |
| 0x2 | 2 | Right Button Pressed |
| 0x4 | 4 | Left Button Pressed |
| 0x8 | 8 | Ornage Button Pressed |
msec = nxt.TimerRead( )
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( )
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 |
| <0x1964 | <6500 | 1 |
| <0x1B58 | <7000 | 2 |
| <0x1D4C | <7500 | 3 |
| >0x1D4C | >7500 | 4 |
The rechargeable battery capacity vs voltage table looks like this:
| Hex | Decimal | Capacity |
|---|---|---|
| <0x1BBC | <7100 | 0 |
| <0x1C20 | <7200 | 1 |
| <0x1C84 | <7300 | 2 |
| <0x1D4C | <7500 | 3 |
| >0x1D4C | >7500 | 4 |
nxt.PowerDown( [x] )
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 at the default value after the program ends.
- x is not supplied
- Unconditionally turns the NXT off. Pressing the orange button on the front panel powers it up again.
- x is 0
- Disables the auto poweroff function
- x is non 0
- Sets the auto power off timer to x minutes
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 )
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.
s = nxt.DisableNXT( [s=0] )
If s is non-zero, disables the NXT driver ISR that runs every 1 msec. If s is 0 or not specified, enables the ISR. Returns the state of the ISR before any changes are made so that it can be set back the way it was.
Note that the timer ticks are still incremented when the ISR is disabled.
This function is provided as a way to disable the NXT driver ISR so that the internal data structures don't change underneath your application. It was provided as a way to get the motor sync operation to work under all conditions.
This function is intended to be used within another function - not the command line. The reason for this is that if you are connected via Bluetooth, and disable the driver from the command line, the underlying driver will never get another character from the Bluetooth system.
To get around this problem, the NXT driver ISR is unconditionally enabled whenever the command line prompt is displayed.
nxt.Reflash( )
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...
nxt.EncodeIR( data, mode )
Takes the three least significant nibbles of data and converts them into a string suitable for sending directly to the .
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 |
Mode 0 is not implemented yet...
In IR Trains Mode (1) the data parameter is decoded in nibbles as follows:
- RC Channel - ranges from 1 to 3
- Command - StepFWD (0) StepREV(1) Stop(2)
- Toggle - Alternate between 0 and any non-0 value
So, if you want to increase the forward speed of the RC Train on Channel 1, you send 0x100. To increase it again, the step must be interpreted as a new command, so set the toggle bit and send out 0x101.
In PF Motors Mode (2) the function automatically calculates the 4th nibble, which is the checksum. To find out more about the IRLink data stream and how each of the specific bits affects the operation, refer to the provided by LEGO.
sum = nxt.Checksum( string, [type=0] )
Returns a checksum of the individual bytes in string. Recall that it's possible to build arbitrary strings with embedded NULLs using the string.byte() that's part of standard Lua.
If type is 0 (the default value) then the value returned is the sum of all the individual bytes in the string.
If type is non-0 then the value returned is the CRC16-XMODEM checksum that is used in the XMODEM protocol.
false/true/nil = nxt.XMODEMSend( string )
Sends string to the console in a format compatible with the XMODEM Protocol. Only one XMODEM session can be active at any time.
Most XMODEM implementations require the user to specify a source file when sending data. On the NXT, this might not make much sense. Instead, the user sets up a destination file name on the host and then just sends data until it's done.
The string should be 128 bytes long - the same size as an XMODEM packet. Longer strings are simply truncated at 128 bytes, and shorter strings are padded with CRTL-Z (0x1A).
The first time the function is called, it sets up an XMODEM state machine and returns TRUE. Subsequent calls return one of three values:
- false - The state machine is running but is not ready for a new string yet.
- true - The state machine is ready for a new string which must be created before calling nxt.XMODEMSend() again.
- nil - The state machine has been stopped either from the user, the host, or excessive errors.
When you are finished sending data and want to terminate the XMODEM state machine, just send the value nil (not an empty string!) and the machine will stop by returning nil as well.
Here's an example for dumping the NXT on-chip FLASH contents:
p = "" addr = 0x100000 repeat local s = nxt.XMODEMSend(p) if( s ) then if addr >= 0x130000 then p = nil else p = nxt.MemRead(addr,128) addr = addr + 128 end end until s == nil
NOTE: Future versions may support sending the data to a different device than the console. For example, you might be connected via Bluetooth, but you want to send data to another NXT via RS485.
false/string/nil = nxt.XMODEMRecv( )
Receives string from the console in a format compatible with the XMODEM Protocol. Only one XMODEM session can be active at any time.
Most XMODEM implementations require the user to specify a destination file when receiving data. On the NXT, this might not make much sense. Instead, the user sets up a source file name on the host and then just receives until it's done.
This allows the user lots of flexibility in handling the data. When the XMODEMRecv() function returns a new string to the application, it is guaranteed to have a good checksum and sequence number. You can store the data in a table, write it to FLASH, or even throw it away.
The first time the function is called, it sets up an XMODEM state machine and returns FALSE. Subsequent calls return one of three values:
- string - The state machine has received a new string from the host. The application must handle the string before calling nxt.XMODEMRecv() again.
- false - The state machine is running but there is no new string available yet.
- nil - The state machine has been stopped either from the user, the host, or excessive errors.
Normally, the protocol terminates when the host has no more data to send. If you want to terminate the process early, just pass a nil to nxt.XMODEMRecv().
Here's an example for receiving a file to an array. Note that this works for about 15-20 K of data on a freshly booted NXT.
a={}
repeat
s=nxt.xmodemRecv()
if s then
table.insert(a,s)
end
until s == nil
NOTE: Future versions may support sending the data to a different device than the console. For example, you might be connected via Bluetooth, but you want to send data to another NXT via RS485.
Bitwise Math API
The Bitwise Math API is deprecated. Use the new bit library in Lua 5.2
Float Math API
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
Trig Float Math API
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 )
Returns a = sin(x)
a = nxt.cos( x )
Returns a = cos(x)
a = nxt.tan( x )
Returns a = tan(x)
a = nxt.asin( x )
Returns a = asin(x)
a = nxt.acos( x )
Returns a = acos(x)
a = nxt.atan( x )
Returns a = atan(x)
a = nxt.atan2( x, y )
Returns a = atan(x/y)
a = nxt.pi( )
Returns the constant pi.
Log Float Math API
The logarithmic functions include operators to do powers, natural and base 10 logarithms, and the square root.
a = nxt.exp( x )
Returns a = e^x
a = nxt.pow( x, y )
Returns a = x^y
a = nxt.log( x )
Returns a = loge(x)
a = nxt.log10( x )
Returns a = log10(x)
a = nxt.sqrt( x )
Returns a = sqrt(x)
Raw Float Math API
These functions convert between floats and integers, and between numbers and their raw representation in memory. Be sure to read the Datalogging With pbLua Tutorial for details on how to use these functions.
x = nxt.float( i, f )
Returns the floating point value that represents i + f/1,000,000
Keep in mind that for negative numbers, both i and f must be negative.
i,f = nxt.toint( x )
Returns the integer and floating point portion of x as standard Lua integers. the floating point portion is scaled to a precision of 1,000,000.
string = nxt.istr( i )
Returns the raw binary data string that represents the integer value i.
This string can be used to save space instead of storing the entire value, or for direct manipulation if you're curious about how 32 bit integers are represented in the ARM architecture.
i = nxt.stri( string )
Returns the integer that is represented by the raw binary data string.
string = nxt.fstr( f )
Returns the raw binary data string that represents the single precision floating point value f.
This string can be used to save space instead of storing the entire value, or for direct manipulation if you're curious about how single precision floats are represented in the ARM architecture.
f = nxt.strf( string )
Returns the single precision float that is represented by the raw binary data string.
Misc Math API
Here’s where we put math functions that don’t really fit anywhere else
a = nxt.random( [range=0, offset=0] )
Returns a random 32 bit value with range and offset depending on the values as described below:
- If range is 0 (default) the random number is between 0 and 2,147,483,647
- If range is non-0 the random number is between 0 and range-1
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.abs( x )
Returns the absolute value of x.
The value of x can be an integer, float, hexadecimal constant, or even a string that can be converted to an integer or float!
If x is an integer, the result is an integer.
If x is a float, the result is a float.
a = nxt.min( x, y )
Returns a = min( x, y )
The values of x and y can integers, floats, hexadecimal constants, or even strings that can be converted to integers or floats!
The resultant types of x and y must be identical.
If x and y are floats, the result is a float
If x and y are integers, the result is an integer
a = nxt.max( x, y )
Returns a = max( x, y )
The values of x and y can integers, floats, hexadecimal constants, or even strings that can be converted to integers or floats!
The resultant types of x and y must be identical.
If x and y are floats, the result is a float
If x and y are integers, the result is an integer
a = nxt.sign( x )
Returns the sign of x as an integer.
The value of x can be an integer, float, hexadecimal constant, or even a string that can be converted to an integer or float!
Returns 0 if x = 0
Returns -1 if x < 0
Returns 1 if x > 0
a = nxt.ceil( x )
Returns a = ceil(x), the smallest integer (as float) larger than or equal to x
a = nxt.floor( x )
Returns a = floor(x), the largest integer (as float) smaller than or equal to x
Display API
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.
When printing a string at a particular location, the top left corner of the bounding box of the string is specified. The display scroll routine shifts the entire display up by one line (8 pixels)
If you design robots where the NXT is “upside down” you can flip the display around.
See the LCD Control Tutorial for examples of the Display API.
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 4×6 font and plan to use it with code that lets you put text at an arbitrary location on the screen.
nxt.DisplayClear( )
Turns all the pixels in the display off.
state = nxt.DisplayFlip( [state] )
Flips the display updside-down so you can read it the other way around. The state parameter works as follows:
- If state is 0, the display is turned the "right" way around
- If state is non-0, the display is turned upside-down
- If state is not supplied, the display orientation is toggled
The function returns the new state of the display orientation.
state = nxt.DisplayInvert( [state] )
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:
- If state is 0, the display pixels are as normal (dark on light)
- If state is non-0, the display pixels are inverted (light on dark)
- If state is not supplied, the display pixels are toggled
The function returns the new state of the display pixels.
nxt.DisplayText( string [, x, y, inv] )
Prints the string to the display with the top left corner of the text box starting at (x, y). If x and x are omitted, the text starts on the bottom left side of the display. If only y is omitted the text starts on the bottom row of the display.
The function also accepts an optional inv parameter that defaults 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( )
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] )
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 )
Returns 1 if the pixel given by the display coordinate pair (x,y) is turned on, and 0 if the pixel is turned off.
Sound API
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( )
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] )
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] )
Starts playing a series of tones specified in the melody string at volume. The volume defaults to 1.
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
Unfortunately, you cannot specify the values as regular integers because the ARM processor inside the NXT is a little-endian processor.
Input Port API
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 API that sends I2C messages to intelligent sensors connected to the input ports.
To make it much easier to process the different kinds of sensors, the Input Port API has been changed as of Beta 18a to let you specify the sensor type and mode when you set call nxt.InputSetType().
See the Basic Sensors Tutorial for some examples that use the basic analog sensors.
nxt.InputSetType( port [, type=0, mode=0] )
Note that this function has changed significantly as of the Beta 18a version of pbLua.
In particular, the type parameter MUST be set according to the table below depending on the sensor type. Previous versions simply used 2 for I2C sensor types - this must be changed to 10 (LOWSPEED) or 11 (LOWSPEED_9V) to work now.
Please review any existing code you may have and change it appropriately.
Sets up the sensor on the specified port according to the type and mode tables below. Note that there is no longer any need to set the I/O pins to operate the sensor in a particular mode.
The default type (if not specified) is 0, or NO_SENSOR. If you pass a type that is outside of the normal range of values, a type of NO_SENSOR is assumed.
| Hex | Decimal | Description |
|---|---|---|
| 0x00 | 0 | NO_SENSOR |
| 0x01 | 1 | SWITCH |
| 0x02 | 2 | TEMPERATURE |
| 0x03 | 3 | REFLECTION |
| 0x04 | 4 | ANGLE |
| 0x05 | 5 | LIGHT_ACTIVE |
| 0x06 | 6 | LIGHT_INACTIVE |
| 0x07 | 7 | SOUND_DB |
| 0x08 | 8 | SOUND_DBA |
| 0x09 | 9 | CUSTOM |
| 0x0A | 10 | LOWSPEED |
| 0x0B | 11 | LOWSPEED_9V |
| 0x0C | 12 | HIGHSPEED |
| 0x0D | 13 | COLORFULL |
| 0x0E | 14 | COLORRED |
| 0x0F | 15 | COLORGREEN |
| 0x10 | 16 | COLORBLUE |
| 0x11 | 17 | COLORNONE |
| 0x12 | 18 | COLOREXIT |
The default mode (if not specified) is 0, or RAWMODE. This is fine in most cases if all you want is raw sensor data. For some sensors, it might be easier to have the firmware present the result as percent of full scale, or to count and debounce the switch presses.
The mode value has a slope parameter in the lower 5 bits of the mode byte. They are not currently documented and should be left as 0's.
| Hex | Decimal | Description |
|---|---|---|
| 0x00 | 0 | NO_SENSOR |
| 0x20 | 32 | BOOLEANMODE |
| 0x40 | 64 | TRANSITIONCNTMODE |
| 0x60 | 96 | PERIODCOUNTERMODE |
| 0x80 | 128 | PCTFULLSCALEMODE |
| 0xA0 | 160 | CELSIUSMODE |
| 0xC0 | 192 | FAHRENHEITMODE |
| 0xE0 | 224 | ANGLESTEPSMODE |
nxt.InputSetValue( port [, value=0] )
Unconditionally sets the value of the sensor at the specified port. If no value is specified, 0 is the default. This function is useful for resetting the value of touch and angle sensors when the are in a mode that returns a counted value.
nxt.InputSetDir( port, dir0, dir1 )
Sets the direction of the digi0 and digi1 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 )
Sets the state of the digi0 and digi1 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.
The state0 and state1 pin values are only applied if the corresponding pin state is set to output.
rawAD, state0, state1, val = nxt.InputGetStatus( port )
Gets the raw A/D value as well as the digi0 and digi1 pin state and the processed result of the sensor data.
The raw A/D value depends on the sensor type and may need to be processed to give a useful value for your application, but in most cases it's better and faster to use the appropriate sensor type and mode when setting up the sensor using the nxt.InputSetType() function.
The digi0 and digi1 pin values are only valid if the corresponding pin state is set to input.
The val represents the processed result, which may take on many different ranges depending on the sensor mode. The only special result value is when the sensor is in full color mode, and in that case val is interpreted as follows:
| Hex | Decimal | Description |
|---|---|---|
| 0x1 | 1 | BLACK |
| 0x2 | 2 | BLUE |
| 0x3 | 3 | GREEN |
| 0x4 | 4 | YELLOW |
| 0x5 | 5 | RED |
| 0x6 | 6 | WHITE |
Low Speed I2C API
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 I2C Sensors Tutorial for some examples that use the basic analog sensors.
nxt.I2CInitPins( port )
Initializes the port pin state for I2C communication. This function must be called once before any communication with I2C devices on a specific port.
Subsequent communication on a port that has been initialized does not require this function to be called.
state, txstatus, rxstatus = nxt.I2CGetStatus( port )
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 | 2tt> | LOWSPEED_TRANSMITTING |
| 0x04 | 4 | LOWSPEED_RECEIVING |
| 0x08 | 8 | LOWSPEED_TEST_WAIT_STATE |
| 0x10 | 16 | LOWSPEED_RESTART_CONDITION |
| 0x20 | 32 | LOWSPEED_WAIT_BEFORE_RX |
The rx and tx status can have three different values as described below:
| Hex | Decimal | Description |
|---|---|---|
| 0x00 | 0 | Busy |
| 0x01 | 1 | Complete |
| 0xFF | 255 | Timeout |
nxt.I2CSendData( port, string [, rxlen=0] )
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 rxlen value is optional, and defaults to 0. If a non-zero value is supplied then the I2C bus does a "repeated start" so that it can read data in the same transaction as a write.
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] )
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 rxlen parameter is optional, and has a maximum value of 16. If rxlen is not specified, then the number of bytes requested in the previous I2CReceiveData() call are returned.
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.
Note that the previous version of this function required the rxlen parameter to be specified, but the newer version determines this automatically.
High Speed RS485 API
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.
rate = nxt.RS485Enable( rate )
Initializes the 485 interface for communication. A value of 0 turns the interface off, and a non-zero value initializes it for communication.
The RS485 interface is set to the lowest standard rate that is greater than or equal to the requested rate, and for 8 data bits, no parity, 1 stop bit. The allowable values for the baud rate are:
The actual data rate that is chosen is returned.
- 1200
- 2400
- 4800
- 9600
- 19200
- 38400
- 57600
- 115000
- 230000
- 460000
- 921600
So if you specify 1000 baud, you'll get 1200. If the requested rate is too high, then the actual rate falls back to the maximum.
nxt.RS485SendData( string )
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( )
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 pbLua's string functions.
Output API
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 Basic Motor Control and Advanced Motor Control tutorials for examples of motor control possibilities.
The motor control API functions all take the motor number as the first parameter. Use the table provided to translate between the motor letter embossed in the NXT over the output ports, and the motor number the API functions need.
| Hex | Decimal | Description |
|---|---|---|
| 0x01 | 1 | Motor Port A |
| 0x02 | 1 | Motor Port B |
| 0x03 | 1 | Motor Port C |
nxt.OutputSetRegulation( motor [, state=0, mode=1, factor=4, divisor=1, offset=20] )
Sets the regulation state and mode for the specified motor. Also sets the factor, divisor and offset for position regulation. These new parameters let you tune the performance of your servo operation if needed. The defaults work well in most cases.
The state parameter affects the type of speed regulation performed by the firmware as described in the table below:
| Hex | Decimal | Description |
|---|---|---|
| 0x00 | 0 | Idle - default, no speed regulation |
| 0x01 | 1 | Regulated - single motor speed regulation |
| 0x02 | 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 |
|---|---|---|
| 0x00 | 0 | Float |
| 0x01 | 1 | Brake - default |
The factor, divisor, and offset work together to shape the performance of the motor as it apporaches the setpoint. Versions of pbLua previous to Beta 18a do not officially support this feature. Here's how it works.
As a motor approaches its position setpoint, it should slow down so that it does not overshoot and have to come back to the target. The quesiton is when should the motor begin to slow down? One idea is to make the motor start slowing down depending on how fast it's going, and that's what we do here.
The motor begins to slow down when it is ((speed*factor)/divisor) ticks away from the setpoint. The speed of the motor is set to a percentage of its original speed depending on how far it is from the setpoint. An example will make this clear...
Let's say that a motor is set to go at speed=50 and we want it to make a single turn of 360 degrees. Based on the default factor=4 and divisor=1, the motor will begin to slow down at a set difference of ((50*4)/1)=200 ticks. The multiplication is done before the division to minimize scaling errors.
So when the motor is within 200 ticks of the target, it begins to slow down. The new set speed is given as:
speed=offset+(((SetSpeed-offset)*curDiff)/setDiff)
This lets the motor slow down gracefully and never get lower than offset, which is important because otherwise the motor could be set to a speed that makes it stall and never get to the target position.
speed, tacho, blocktacho, runstate, overload, rotcount, torun = nxt.OutputGetStatus( motor )
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 on the different values that runstate can have.
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 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 absolute difference between the current and 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. Remember, it's the absolute value of the difference, so you don't have to take into account the direction the motor is turning. If you are running the motor at a set speed with no target tacho value, the torun value is 0.
nxt.OutputResetTacho( motor [, tacho=0, block=0, rot=0] )
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 any parameter will reset the specified tacho counter as follows:
- tacho != 0 resets the running TachoCount
- block != 0 resets the BlockTachoCount
- rot != 0 resets the RotationCount
See nxt.OutputGetMotorStatus() for a detailed decription of the tacho values.
nxt.OutputSetSpeed( motor [, runstate=0, speed=0, tacho=0, turn=0] )
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 | 128 | 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.
Passing an invalid runstate produces undefined behaviour!
The speed parameter controls the rotation speed of the motor. Positive numbers are forwards, negative numbers are reverse. The speed is limited to the range +/- 100 by this function, so it's OK to pass in larger values.
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.
nxt.OutputSetPID( motor [, P, I, D] )
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.
The P,I,D values are optional - if you omit them the default values from the standard LEGO firmware are used, as follows:
- P default value is 96
- I default value is 32
- D default value is 32
In general you don't need to mess with this - so don't. But if you do want to play around with these numbers, bear in mind that the LEGO firmware divides all of these values by 32 using integer math, so that only multiples of 32 are valid. More precisely, no matter what value you specify, the firmware will use the result of the truncated division by 32. In practical terms, this means that values from 32 to 63 will give the same results.
Bluetooth API
The NXT Bluetooth API is now fairly stable. We’ve got enough capability to read raw streams of information from a Blutooth GPS and display them on the LCD – which is pretty cool!
That being said, it can be a real challenge to get a Bluetooth based application running on the NXT. In most cases, you’ll be using the incoming Bluetooth channel as the console, so sending and receiving data from other NXTs will not be easy to do.
See the sample program pages for examples of how to use the Bluetooth functions effectively, and be sure to look at the Bluetooth GPS for NXT for more details on using the Bluetooth API.
nxt.BtFactoryReset( )
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.
This statement only begins the factory reset process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and wait until the state returns to UPD_IDLE before executing another BlueTooth function.
nxt.BtPower( [state=1] )
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.
This statement only begins the power change process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and wait until the state returns to UPD_IDLE before executing another BlueTooth function.
state, active, update = nxt.BtGetStatus( )
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 - powered 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/tt> |
| 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. They include the following values from the BlueTooth driver code:
VarsComm.BtState VarsComm.BtBcPinLevel IOMapComm.BrickData.BtStateStatus IOMapComm.BrickData.BtHwStatus BtIgnoreLength
nxt.BtVisible( [state=1] )
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.
This statement only begins the visibility change process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and wait until the state returns to UPD_IDLE before executing another BlueTooth function.
nxt.BtSearch( [state=1] )
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 of about 20 seconds if no new Bluetooth device has been found, and the search mode is automatically aborted.
This statement only begins the search process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and wait until the state returns to UPD_IDLE before executing another BlueTooth function.
name, class, addr, status = nxt.BtGetDeviceEntry( idx )
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 |
| 0x2 | 2 | 2.5mW - Range ~10m |
| 0x3 | 3 | 1mW - Range ~1m |
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 )
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 )
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 )
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.
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 )
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 connect process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and wait until the state returns to UPD_IDLE before executing another BlueTooth function.
nxt.BtDisconnect( con )
Disconnects the Bluetooth connection indexed by con.
This statement only begins the disconnect process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and wait until the state returns to UPD_IDLE before executing another BlueTooth function.
nxt.BtDisconnectAll( )
Disconnects all the BLuetooth connections.
This statement only begins the disconnect process. You need to read the status of the Bluetooth system by calling nxt.BtGetStatus() and wait until the state returns to UPD_IDLE before executing another BlueTooth function.
nxt.BtStreamMode( [ignore=0] )
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. This is the normal operating mode of the standard LEGO firmware, which is why you cannot easily parse things like raw data from a Bluetooth GPS.
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. This is very useful when you want to parse things like raw data from a Bluetooth GPS.
nxt.BtStreamSend( con, string [, reply=0, timeout=30000] )
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.
string = nxt.BtStreamRecv( )
Returns the bytes received to this point by the Bluetooth subsystem as a string. If there are no bytes ready, then returns NIL.
File API
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!
Make sure you read the FLASH File System Tutorial and the Datalogging With pbLua Tutorial for all the details on how to use the these functions. 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 strings 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 “#$%@&^%” text=then go ahead, but why would you really want to do that?
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.
Again, make sure you read the FLASH File System Tutorial and the Datalogging With pbLua Tutorial for all the details on how to use the these functions.
b = nxt.FileExists( name )
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 )
Opens the file given by name for reading. The call can fail if:
- The file name is too long
- The file does not exist
- The file is already open for writing
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=1024] )
Opens the file given by name and allocates maxbytes of space for the file. The call can fail if:
- The file name is too long
- The file already exists
- There is no space left in the FLASH
- There are no more file handles left
If the call fails, then the interpreter is immediately stopped and an error message is printed on the console if one is connected.
The maxbytes parameter is optional and defaults to 1024.
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" table=1024 )
bytes = nxt.FileWrite( handle, string )
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 special file handle "0" (zero) can be used to write data from the console, which is helpful if you want to send raw data bytes to the console without the CR-LF that is tacked on by the standard print command.
The call can fail if:
- The file handle is invalid
- The file is not open for writing
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" table=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] )
Returns the string of characters from the file indicated by handle depending on the value of the second parameter. Returns nil if there are no more characters in the file.
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:
- The file handle is invalid
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 LF will terminate the line, and CR is ignored.
-- 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 )
Attempts to set the file pointer of the file indicated by handle to the value given by offset. The call can fail if:
- The file handle is invalid
- The file is not open for reading, or is currently open for writing
- The offset is beyond the end of the file
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 )
Attempts to close the file descriptor of the file indicated by handle The call can fail if:
- The file handle is invalid
- The file is not open for reading or writing
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 )
Deletes the file given by name. The call can fail if:
- The file name is too long
- The file does not exist
- The file is still open for writing
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 )
Returns information about the file handles (descriptors) as follows:
- type
- The type of the file header
Value Description 1 Free header ready for a new file 2 Erased header that cannot be used 3 The Master File System Header 4 A file header that is open for writing 5 A file header that can used for reading - name
- The name of the file
- block
- The logical block number of the file within the FLASH memory allocated to the file system. This is not the absolute block number of the file in the entire device FLASH.
- maxBytes
- The maximum number of bytes that the files has. If the file is open for writing and has not been closed, this will be 0xFFFF or 65535.
- curBytes
- The number of bytes that have been written to the file so far if the file is open for writing. The number of bytes that have been read so far if the file is open for reading. The value is 0 if the file is erased or closed.
- curPtr
- The absolute address in the device address space where the next byte will be read from or written to if the file is open for reading or wrinting respectively. If the file is closed, returns the absolute address in the device address space of the first byte of the file. If the file is deleted or non-existent, returns 0.
The call can fail if:
- The file handle is invalid
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 )
Returns information about the FLASH file system as follows:
- files
- The number of files allowed in a fully utilized master file descriptor. This is currently 31 files.
- blocks
- The total number of blocks allocated to the FLASH file system
- blockSize
- The size of one block in the FLASH file system
status = nxt.FileFormat( type )
Formats the file system according to the type parameter as follows:
| Format | Descripton |
|---|---|
| 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.
string|chunk = nxt.FileChooser( [operation=name, timeout=15] )
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 an operation, the function returns the filename of the selected file, or nil if no file is selected.
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 extended 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:
- The file is still open for writing
- There is a syntax or other error in the file
If you do not want the user to have to choose a file, you can always use the loadfile() and dofile() functions to operate on files programatically.
chunk = nxt.loadfile( name )
Loads the file given by name and returns a Lua "chunk" that can be executed later. Please refer to the sections on and the for more details on how to use this function.
The call can fail if:
- The file name is too long
- The file does not exist
- The file is still open for writing
- There is a syntax or other error in the file
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 )
Loads and executes the file given by name. Please refer to the section on for more details on how to use this function.
The call can fail if:
- The file name is too long
- The file does not exist
- The file is still open for writing
- There is a syntax or other error in the file
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!
Memory API
These functions round out the NXT API with operations that may be useful during debugging, especially when you run out of memory. You can also use these functions to read and write arbitrary RAM and FLASH adresses – which you should do with extreme caution.
TotalEntries, UsedEntries, FreeEntries, TotalBlocks, UsedBlocks, FreeBlocks = nxt.HeapInfo( [force=0] )
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.
If the force parameter is non-zero, then this function dumps the heap map to the console like so:
> nxt.HeapInfo(1) Dumping the mm_heap... |0x00201d80|B 0|NB 1|PB 0|Z 1|NF 2575|PF 0| |0x00201d88|B 1|NB 43|PB 0|Z 42| |0x00201ed8|B 43|NB 68|PB 1|Z 25| ... |0x00206da8|B 2565|NB 2566|PB 2564|Z 1| |0x00206db0|B 2566|NB 2567|PB 2565|Z 1|NF 2518|PF 2611| |0x00206db8|B 2567|NB 2570|PB 2566|Z 3| |0x00206dd0|B 2570|NB 2575|PB 2567|Z 5| |0x00206df8|B 2575|NB 2580|PB 2570|Z 5|NF 2599|PF 0| |0x00206e20|B 2580|NB 2590|PB 2575|Z 10| |0x00206e70|B 2590|NB 2595|PB 2580|Z 5| |0x00206e98|B 2595|NB 2599|PB 2590|Z 4| |0x00206eb8|B 2599|NB 2600|PB 2595|Z 1|NF 2606|PF 2575| |0x00206ec0|B 2600|NB 2603|PB 2599|Z 3| |0x00206ed8|B 2603|NB 2606|PB 2600|Z 3| |0x00206ef0|B 2606|NB 2608|PB 2603|Z 2|NF 2611|PF 2599| |0x00206f00|B 2608|NB 2611|PB 2606|Z 3| |0x00206f18|B 2611|NB 2612|PB 2608|Z 1|NF 2566|PF 2606| |0x00206f20|B 2612|NB 2615|PB 2611|Z 3| |0x00206f38|B 2615|NB 2621|PB 2612|Z 6| |0x00206f68|B 2621|NB 0|PB 2615|Z 4179|NF 0|PF 136| Total Entries 505 Used Entries 480 Free Entries 25 Total Blocks 6799 Used Blocks 2581 Free Blocks 4218
Here's what the values returned by the function mean:
- TotalEntries
- The total number of used and free entries in the heap
- UsedEntries
- The number of entries in use by pbLua
- FreeEntries
- The number of entries available for use by pbLua
- TotalBlocks
- The total number of used and free 8 byte blocks in the heap
- UsedBlocks
- The number of 8 byte blocks in use by pbLua
- FreeBlocks
- The number of 8 byte blocks available for use by pbLua
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.
Currently, the total number of blocks available for use is about 6,800 or 54,400 bytes.
UserFlashStart, FlashStart, FlashEnd, RamStart, RamEnd = nxt.MemInfo( )
Returns a number of useful values for system memory spaces, the most
useful being UserFlashStart. The typical values as of Beta 15p
are:
| Name | Hex | Decima |
|---|---|---|
| UserFlashStart | 0x00128900 | 1214720 |
| FlashStart | 0x00100000 | 1048576 |
| FlashEnd | 0x00140000 | 1310720 |
| RamStart | 0x00200000 | 2097152 |
| RamEnd | 0x00210000 | 2162688 |
s = nxt.MemRead( addr, length )
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 )
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. Writes to processor registers are disabled.