Module Reference¶
All Lizard modules have the following methods in common.
Methods | Description |
---|---|
module.mute() |
Turn output off |
module.unmute() |
Turn output on |
module.shadow() |
Send all method calls also to another module |
module.broadcast() |
Regularly send properties to another microcontroller (for internal use) |
Shadows are useful if multiple modules should behave exactly the same, e.g. two actuators that should always move synchronously.
The broadcast
method is used internally with port expanders.
Core¶
The core module encapsulates various properties and methods that are related to the microcontroller itself. It is automatically created right after the boot sequence.
Properties | Description | Data type |
---|---|---|
core.debug |
Whether to output debug information to the command line | bool |
core.millis |
Time since booting the microcontroller (ms) | int |
core.heap |
Free heap memory (bytes) | int |
Methods | Description | Arguments |
---|---|---|
core.restart() |
Restart the microcontroller | |
core.version() |
Show lizard version | |
core.info() |
Show lizard version, compile time and IDF version | |
core.print(...) |
Print arbitrary arguments to the command line | arbitrary |
core.output(format) |
Define the output format | str |
core.startup_checksum() |
Show 16-bit checksum of the startup script |
The output format
is a string with multiple space-separated elements of the pattern <module>.<property>[:<precision>]
or <variable>[:<precision>]
.
The precision
is an optional integer specifying the number of decimal places for a floating point number.
For example, the format "core.millis input.level motor.position:3"
might yield an output like "92456 1 12.789
.
Bluetooth¶
Lizard can receive messages via Bluetooth Low Energy, and also send messages in return to a connected device. Simply create a Bluetooth module with a device name of your choice.
Constructor | Description | Arguments |
---|---|---|
bluetooth = Bluetooth(device_name) |
initialize bluetooth with advertised device_name |
str |
bluetooth.send(data) |
send data via notification |
str |
Lizard will offer a service 23014CCC-4677-4864-B4C1-8F772B373FAC and a characteristic 37107598-7030-46D3-B688-E3664C1712F0 that allows writing Lizard statements like on the command line. On a second characteristic 19f91f52-e3b1-4809-9d71-bc16ecd81069 notifications will be emitted when send(data)
is executed.
Input¶
The input module is associated with a digital input pin that is be connected to a pushbutton, sensor or other input signal.
Constructor | Description | Arguments |
---|---|---|
input = Input(pin) |
pin is the corresponding GPIO number |
int |
Properties | Description | Data type |
---|---|---|
input.level |
Current signal level (0 or 1) | int |
input.change |
Level change since last cycle (-1..1) | int |
Methods | Description |
---|---|
input.get() |
Output the current level |
input.pullup() |
Add an internal pull-up resistor |
input.pulldown() |
Add an internal pull-down resistor |
input.pulloff() |
Remove internal pull resistor |
Output¶
The output module is associated with a digital output pin that is connected to an LED, actuator or other output signal.
Constructor | Description | Arguments |
---|---|---|
output = Output(pin) |
pin is the corresponding GPIO number |
int |
Properties | Description | Data type |
---|---|---|
output.level |
Current signal level (0 or 1) | int |
output.change |
Level change since last cycle (-1..1) | int |
Methods | Description | Arguments |
---|---|---|
output.on() |
Set the output pin high | |
output.off() |
Set the output pin low | |
output.level(value) |
Set the output level to the given value |
bool |
output.pulse(interval[, duty_cycle]) |
Switch output on and off | float s |
The pulse()
method allows pulsing an output with a given interval in seconds and an optional duty cycle between 0 and 1 (0.5 by default).
Note that the pulsing frequency is limited by the main loop to around 20 Hz.
PWM Output¶
The PWM output module is associated with a digital output pin that is connected to an LED, actuator or other output signal.
Constructor | Description | Arguments |
---|---|---|
pwm = PwmOutput(pin) |
pin is the corresponding GPIO number |
int |
Properties | Description | Data type |
---|---|---|
output.duty |
Duty cycle (8 bit: 0..256, default: 128) | int |
output.frequency |
Frequency (Hz, default: 1000) | int |
Methods | Description | Arguments |
---|---|---|
output.on() |
Turn on the PWM signal | |
output.off() |
Turn off the PWM signal |
MCP23017 Port Expander¶
The MCP23017 allows controlling up to 16 general purpose input or output pins via I2C.
Constructor | Description | Arguments |
---|---|---|
mcp = Mcp23017([port[, sda[, scl[, address[, clk_speed]]]]]) |
See below | int s |
The constructor expects up to five arguments:
port
: 0 or 1, since the ESP32 has two I2C ports (default: 0)sda
: SDA pin (default: 21)scl
: SCL pin (default: 22)address
: client address of the MCP (0x20..0x28, default: 0x20)clk_speed
: I2C clock speed (default: 100000)
Properties | Description | Data type |
---|---|---|
mcp.levels |
Levels of all 16 pins | int |
mcp.inputs |
Input mode of all 16 pins | int |
mcp.pullups |
Pull-up resistors for all 16 pins | int |
The properties levels
, inputs
and pullups
contain binary information for all 16 pins in form of a 16 bit unsigned integer.
Methods | Description | Arguments |
---|---|---|
mcp.levels(value) |
Set levels of all 16 pins | int |
mcp.inputs(value) |
Set input mode of all 16 pins | int |
mcp.pullups(value) |
Set pull-up resistors for all 16 pins | int |
The methods levels()
, inputs()
and pullups()
expect a 16 bit unsigned integer value
containing binary information for all 16 pins.
Use inputs()
to configure input and output pins, e.g. inputs(0xffff)
all inputs or inputs(0x0000)
all outputs.
While levels()
will only affect output pins, pullups()
will only affect the levels of input pins.
Using an MCP23017 port expander module you can not only access individual pins.
You can also instantiate the following modules passing the mcp
instance as the first argument:
- Input:
input = Input(mcp, pin)
- Output:
output = Output(mcp, pin)
- Linear motor:
motor = LinearMotor(mcp, move_in, move_out, end_in, end_out)
The pins pin
, move_in
, move_out
, end_in
and end_out
are numbers from 0 to 15 referring to A0...A7 and B0...B7 on the MCP23017.
IMU¶
The IMU module provides access to a Bosch BNO055 9-axis absolute orientation sensor. Currently, only reading the accelerometer is implemented.
Constructor | Description | Arguments |
---|---|---|
imu = Imu([port[, sda[, scl[, address[, clk_speed]]]]]) |
See below | int s |
The constructor expects up to five arguments:
port
: 0 or 1, since the ESP32 has two I2C ports (default: 0)sda
: SDA pin (default: 21)scl
: SCL pin (default: 22)address
: client address of the MCP (0x28 or 0x29, default: 0x28)clk_speed
: I2C clock speed (default: 100000)
Properties | Description | Data type |
---|---|---|
imu.acc_x |
acceleration in x direction (m/s^2) | float |
imu.acc_y |
acceleration in y direction (m/s^2) | float |
imu.acc_z |
acceleration in z direction (m/s^2) | float |
imu.roll |
roll (degrees, see datasheet) | float |
imu.pitch |
pitch (degrees, see datasheet) | float |
imu.yaw |
yaw (degrees, see datasheet) | float |
imu.quat_w |
quaternion component w | float |
imu.quat_x |
quaternion component x | float |
imu.quat_y |
quaternion component y | float |
imu.quat_z |
quaternion component z | float |
imu.cal_sys |
calibration of system (0 to 3) | float |
imu.cal_gyr |
calibration of gyroscope (0 to 3) | float |
imu.cal_acc |
calibration of accelerometer (0 to 3) | float |
imu.cal_mag |
calibration of magnetometer (0 to 3) | float |
CAN interface¶
The CAN module allows communicating with peripherals on the specified CAN bus.
Constructor | Description | Arguments |
---|---|---|
can = Can(rx, tx, baud) |
RX/TX pins and baud rate | int , int , int |
Methods | Description | Arguments |
---|---|---|
can.send(node_id, d0, d1, d2, d3, d4, d5, d6, d7) |
Send a frame with 8 data bytes | 9x int |
can.get_status() |
Print the driver status | |
can.start() |
Start the driver | |
can.stop() |
Stop the driver | |
can.recover() |
Recover the driver |
The method get_status()
prints the following information:
state
("STOPPED", "RUNNING", "BUS_OFF" or "RECOVERING"),msgs_to_tx
,msgs_to_rx
,tx_error_counter
,rx_error_counter
,tx_failed_count
,rx_missed_count
,rx_overrun_count
,arb_lost_count
andbus_error_count
.
After creating a CAN module, the driver is started automatically.
The start()
and stop()
methods are primarily for debugging purposes.
Serial interface¶
The serial module allows communicating with peripherals via the specified connection.
Constructor | Description | Arguments |
---|---|---|
serial = Serial(rx, tx, baud, num) |
RX/TX pins, baud rate, UART number | 4x int |
Methods | Description | Arguments |
---|---|---|
serial.send(b0, b1, b2, ...) |
Send a number of bytes | int s |
serial.read() |
Read and output current buffer |
This module might be used by other modules that communicate with peripherals via serial. You can, however, unmute the serial module to have incoming messages printed to the command line instead of keeping them buffered for other modules.
Linear motor¶
This module controls a linear actuator via two output pins (move in, move out) and two input pins reading two limit switches (end in, end out).
Constructor | Description | Arguments |
---|---|---|
motor = LinearMotor(move_in, move_out, end_in, end_out) |
motor control pins and limit switches | 4x int |
Properties | Description | Data type |
---|---|---|
motor.in |
Motor is in "in" position | bool |
motor.out |
Motor is in "out" position | bool |
Methods | Description |
---|---|
motor.in() |
Move in |
motor.out() |
Move out |
motor.stop() |
Stop motor |
ODrive Motor¶
The ODrive motor module controls a motor using an ODrive motor controller.
Constructor | Description | Arguments |
---|---|---|
motor = ODriveMotor(can, can_id) |
CAN module and node ID | CAN module, int |
Properties | Description | Data type |
---|---|---|
motor.position |
Motor position (meters) | float |
motor.tick_offset |
Encoder tick offset | float |
motor.m_per_tick |
Meters per encoder tick | float |
motor.reversed |
Reverse motor direction | bool |
Methods | Description | Arguments |
---|---|---|
motor.zero() |
Set current position as zero position | |
motor.power(torque) |
Move with given torque |
float |
motor.speed(speed) |
Move with given speed (m/s) |
float |
motor.position(position) |
Move to given position (m) |
float |
motor.limits(speed, current) |
Set speed (m/s) and current (A) limits | float , float |
motor.off() |
Turn motor off (idle state) |
ODrive Wheels¶
The ODrive wheels module combines to ODrive motors and provides odometry and steering for differential wheeled robots.
Constructor | Description | Arguments |
---|---|---|
wheels = ODriveWheels(left_motor, left_motor) |
Two ODrive motor modules | two ODrive motor modules |
Properties | Description | Data type |
---|---|---|
wheels.width |
wheel distance (m) | float |
wheels.linear_speed |
Forward speed (m/s) | float |
wheels.angular_speed |
Turning speed (rad/s) | float |
wheels.enabled |
Whether motors react to commands | bool |
Methods | Description | Arguments |
---|---|---|
wheels.power(left, right) |
Move with torque per wheel | float , float |
wheels.speed(linear, angular) |
Move with linear /angular speed (m/s, rad/s) |
float , float |
wheels.off() |
Turn both motors off (idle state) |
When the wheels are not enabled
, power
and speed
method calls are ignored.
This allows disabling the wheels permanently by setting enabled = false
in conjunction with calling the off()
method.
Now the vehicle can be pushed manually with motors turned off, without taking care of every line of code potentially re-activating the motors.
RMD Motor¶
The RMD motor module controls a Gyems RMD motor via CAN.
Constructor | Description | Arguments |
---|---|---|
rmd = RmdMotor(can, motor_id, ratio) |
CAN module, motor ID (1..8) and transmission ratio | CAN module, int , int |
Properties | Description | Data type |
---|---|---|
rmd.position |
Multi-turn motor position (deg) | float |
rmd.torque |
Current torque | float |
rmd.speed |
Current speed (deg/s) | float |
rmd.temperature |
Current temperature (C) | float |
rmd.can_age |
Time since last CAN message from motor (s) | float |
Methods | Description | Arguments |
---|---|---|
rmd.power(torque) |
Move with given torque (-32..32 A) |
float |
rmd.speed(speed) |
Move with given speed (deg/s) |
float |
rmd.position(pos) |
Move to and hold at pos (deg) |
float |
rmd.position(pos, speed) |
Move to and hold at pos (deg) with max. speed (deg/s) |
float , float |
rmd.stop() |
Stop motor (but keep operating state) | |
rmd.off() |
Turn motor off (clear operating state) | |
rmd.hold() |
Hold current position | |
rmd.get_pid() |
Print PID parameters Kp/Ki for position/speed/torque loop | |
rmd.set_pid(...) |
Set PID parameters Kp/Ki for position/speed/torque loop | 6x int |
rmd.get_acceleration() |
Print acceleration (deg/s^2) | |
rmd.set_acceleration(...) |
Set accelerations/decelerations for position/speed loop (deg/s^2) | 4x int |
rmd.clear_errors() |
Clear motor error | |
rmd.zero() |
Write position to ROM as zero position (see below) |
The zero command
The zero()
method should be used with care!
In contrast to other commands it blocks the main loop for up to 200 ms and requires restarting the motor to take effect.
Furthermore, multiple writes will affect the chip life, thus it is not recommended to use it frequently.
Set acceleration
Although get_acceleration()
prints only one acceleration per motor, set_acceleration
distinguishes the following four parameters:
- acceleration for position mode
- deceleration for position mode
- acceleration for speed mode
- deceleration for speed mode
You can pass 0
to skip parameters, i.e. to keep individual acceleration values unchanged.
RMD Motor Pair¶
The RMD motor pair module allows to synchronize two RMD motors.
Constructor | Description | Arguments |
---|---|---|
rmd = RmdPair(rmd1, rmd2) |
Two RMD motor modules | 2x RMD Motor module |
Properties | Description | Data type |
---|---|---|
rmd.v_max |
Maximum speed (deg/s, default: 360) | float |
rmd.a_max |
Maximum acceleration (deg/s² (default: 10000) | float |
Methods | Description | Arguments |
---|---|---|
rmd.move(x, y) |
Move motor 1 to x and motor 2 to x |
2x float |
rmd.stop() |
Stop motors (but keep operating state) | |
rmd.off() |
Turn motors off (clear operating state) | |
rmd.hold() |
Hold current positions | |
rmd.clear_errors() |
Clear motor errors |
RoboClaw¶
The RoboClaw module serves as building block for more complex modules like RoboClaw motors. It communicates with a Basicmicro RoboClaw motor driver via serial.
Constructor | Description | Arguments |
---|---|---|
claw = RoboClaw(serial, address) |
Serial module and address | Serial module, int |
Properties | Description | Data type |
---|---|---|
claw.temperature |
Board temperature (degrees Celsius) | float |
The temperature property is updated every 1 second.
RoboClaw Motor¶
The RoboClaw motor module controls a motor using a RoboClaw module.
Constructor | Description | Arguments |
---|---|---|
motor = RoboClawMotor(claw, motor_id) |
RoboClaw module and motor ID (1..2) | RoboClaw module, int |
Properties | Description | Data type |
---|---|---|
motor.position |
Multi-turn motor position (encoder ticks) | int |
Methods | Description | Arguments |
---|---|---|
motor.power(torque) |
Move with given torque (-1..1) |
float |
motor.speed(speed) |
Move with given speed (-32767..32767) |
float |
motor.zero() |
Store position as zero position |
RoboClaw Wheels¶
The RoboClaw wheels module combines two RoboClaw motors and provides odometry and steering for differential wheeled robots.
Constructor | Description | Arguments |
---|---|---|
wheels = RoboClawWheels(left_motor, left_motor) |
left and right motors | two RoboClaw motor modules |
Properties | Description | Data type |
---|---|---|
wheels.width |
wheel distance (m) | float |
wheels.linear_speed |
Forward speed (m/s) | float |
wheels.angular_speed |
Turning speed (rad/s) | float |
wheels.m_per_tick |
Meters per encoder tick | float |
wheels.enabled |
Whether motors react to commands | bool |
Methods | Description | Arguments |
---|---|---|
wheels.power(left, right) |
Move with torque per wheel (-1..1) | float , float |
wheels.speed(linear, angular) |
Move with linear /angular speed (m/s, rad/s) |
float , float |
wheels.off() |
Turn both motors off (idle state) |
When the wheels are not enabled
, power
and speed
method calls are ignored.
Stepper Motor¶
The stepper motor module controls a stepper motor via "step" and "direction" pins. It uses the ESP LED Control API to generate pulses with sufficiently high frequencies and the Pulse Counter API to count steps.
Constructor | Description | Arguments |
---|---|---|
motor = StepperMotor(step, dir[, pu[, cp[, lt[, lc]]]]) |
Step and direction pins | 6x int |
The constructor arguments pu
(pulse counter unit), pc
(pulse counter channel), lt
(LED timer) and lc
(LED channel) are optional and default to 0.
When using multiple stepper motors, they can be set to different values to avoid conflicts.
Properties | Description | Data type |
---|---|---|
motor.position |
Motor position (steps) | int |
motor.speed |
Motor speed (steps per second) | int |
motor.idle |
Motor idle state | bool |
Methods | Description | Arguments |
---|---|---|
motor.speed(speed[, acceleration]) |
Move with given speed |
2x float |
motor.position(position, speed[, acceleration]) |
Move to given position |
3x float |
motor.stop() |
Stop |
The optional acceleration argument defaults to 0, which starts and stops pulsing immediately.
Motor Axis¶
The motor axis module wraps a stepper motor and two limit switches. It prevents the motor from moving past the limits. But in contrast to a simple Lizard rule, it allows to actively move out of the limits when moving in the right direction.
Constructor | Description | Arguments |
---|---|---|
axis = MotorAxis(motor, limit1, limit2) |
StepperMotor and Input modules | 3 modules |
Currently the motor axis module has no properties. To get the current position or speed, access the StepperMotor module instead.
Methods | Description | Arguments |
---|---|---|
motor.speed(speed[, acceleration]) |
Move with given speed |
2x float |
motor.position(position, speed[, acceleration]) |
Move to given position |
3x float |
motor.stop() |
Stop |
CanOpenMaster¶
The CanOpenMaster module sends periodic SYNC messages to all CANopen nodes. At creation, no messages are sent until sync_interval
is set to a value greater than 0.
Constructor | Description | Arguments |
---|---|---|
co_master = CanOpenMaster(can) |
CAN module | CAN module |
Properties | Description | Data type |
---|---|---|
co_master.sync_interval |
Amount of lizard steps inbetween each SYNC | int |
CanOpenMotor¶
The CanOpenMotor module implements a subset of commands necessary to control a motor implementing DS402. Positional and velocity units are currently undefined and must by manually measured. Once the configuration sequence has finished, current status, position and velocity are queried on every SYNC.
Constructor | Description | Arguments |
---|---|---|
motor = CanOpenMotor(can, node_id) |
CAN module and node ID (1..127) | CAN module , int |
Methods | Description | Arguments |
---|---|---|
motor.enter_pp_mode(velo) |
Set 402 operating mode to profile position, halt off, and target velocity to velo |
int |
motor.enter_pv_mode() |
Set 402 operating mode to profile velocity, halt on, and target velocity to velo |
int |
motor.set_target_position(pos) |
Set target position to pos (signed). [pp mode] |
int |
motor.commit_target_position() |
Instruct motor to move to previously set target position. [pp mode] | |
motor.set_target_velocity(velo) |
Set target velocity to velo . Absolute for pp mode, signed for pv mode |
int |
motor.set_ctrl_halt(mode) |
Latches / resets the "halt" bit and sends the updated control word to the node | bool |
motor.set_ctrl_enable(mode) |
Latches / resets the "enable operation" bit and sends an updated control word to the node | bool |
motor.reset_fault() |
Clear any faults (like positioning errors). Implicitly sets the "halt" bit. | |
motor.sdo_read(index) |
Performs an SDO read at index index and sub index 0x00 |
int |
Properties | Description | Data type |
---|---|---|
initialized |
Concurrent init sequence has finished, motor is ready | bool |
last_heartbeat |
Time in µs since bootup when last heartbeat was received | int |
is_booting |
Node is in booting state | bool |
is_preoperational |
Node is in pre-operational state | bool |
is_operational |
Node is in operational state | bool |
actual_position |
Motor position at last SYNC | int |
position_offset |
Offset implicitly added to target/reported position | int |
actual_velocity |
Motor velocity at last SYNC | int |
status_enabled |
Operation enabled bit of status word since last SYNC | bool |
status_fault |
Fault bit of status word since last SYNC | bool |
status_target_reached |
Target reached bit of status word since last SYNC | bool |
ctrl_enable |
Latched operation enable bit of every sent control word | bool |
ctrl_halt |
Latched halt bit of every sent control word | bool |
Configuration sequence
After creation of the module, the configuration is stepped through automatically on each heartbeat; once finished, the initialized
attribute is set to true
. Note that for runtime variables (actual position, velocity, and status bits) to be updated, a CanOpenMaster module must exist and be sending periodic SYNCs.
Target position sequence
Note: The target velocity must be positive regardless of target point direction. The halt bit is cleared when entering pp, though it can be set at any point during moves to effectively apply brakes.
// First time, assuming motor is disabled and not in pp mode
motor.set_ctrl_enable(true)
motor.enter_pp_mode(<some positive velocity>)
// All further set points only need these
motor.set_target_position(<some position>)
motor.commit_target_position()
Target velocity sequence
Unlike in the profile position mode, here the sign of the velocity does controls the direction. The halt bit is set when entering pv. To start moving, clear it (and set again to stop).
// First time, assuming motor is disabled and not in pv mode
motor.set_ctrl_enable(true)
motor.enter_pv_mode(<some signed velocity>)
// Further movements only need these
motor.set_ctrl_halt(false)
// await some condition
motor.set_ctrl_halt(true)
Expander¶
The expander module allows communication with another microcontroller connected via serial.
Constructor | Description | Arguments |
---|---|---|
expander = Expander(serial, boot, enable) |
Serial module and boot/enable pins | Serial module, 2x int |
Methods | Description | Arguments |
---|---|---|
expander.run(command) |
Run any command on the other microcontroller |
string |
expander.disconnect() |
Disconnect serial connection and pins | |
expander.flash() |
Flash other microcontroller with own binary data |
The disconnect()
method might be useful to access the other microcontroller on UART0 via USB while still being physically connected to the main microcontroller.
Note that the expander forwards all other method calls to the remote core module, e.g. expander.info()
.
Proxy¶
-- This module is mainly for internal use with the expander module. --
Proxy modules serve as handles for remote modules running on another microcontroller.
Declaring a module x = Proxy()
will allow formulating rules like when x.level == 0 then ...
.
It will receive property values from a remote module with the same name x
, e.g. an input signal level.
Note that the remote module has to have turned on broadcasting: x.broadcast()
.
Constructor |
---|
module = Proxy() |
Note that the proxy module forwards all method calls to the remote module.