Home page / LegoŽ / Robotics / emulegOS / User's Guide |
Disclaimer
This page is not connected with or endorsed by The LEGO Company. LEGO,
LEGOLAND, LEGO SYSTEM, LEGO TECHNIC, DUPLO, LEGO PRIMO and LEGO MINDSTORMS are
trademarks of The LEGO Company.
emulegOS User's Guide
IMPORTANT: Current documentation is the in README distributed with the release.
The following documentation is for the old 0.1.7 compatible version of emuLegOS.
Simply download the proper version of emulegOS for your compiler/IDE and unzip/untar it to a directory of your choice.
emulegOS includes:
makefile / emurcx.bpr : the project makefiles that you find respectively in the Tcl/Tk and in the C++Builder version. You should not need any change here.
tkAppInit.cpp / emurcx.cpp : the application entry points respectively for the Tcl/Tk and the C++Builder version. You should not need any change here.
Main (.cpp + .h) (everything pertaining to the GUI): you don't need to modify them, unless you want to port emulegOS to a different platform, but this is covered by a specific documentation.
rcx.tcl and rcx.tk (Tcl/Tk version only): rcx.tcl is the Tcl code which contains the Callbacks, and Commands for button presses (basically the Tcl api to emulegos), while rcx.tk uses rcx.tcl to display a gui.
emuLegOS (.cpp + .h) : this unit should never be touched, unless you find some bug to correct or want to extend emulation to currently unsupported legOS functions.
Program (.cpp + .h) (C++Builder version only): the place where to put your legOS code. The Tcl/Tk version uses the environment variable RCX_SOURCE to find your code.
RealWorld (.cpp + .h) : you may use this unit to emulate some mechanical behavior of your robot, typically to link sensors to motors.
roboroom.tcl (Tcl/Tk version only): you can have multiple robots interact. Robots can connect to each other through a IR chat room, roboroom.tcl. When one transmits that message is sent to the roboroom which then forwards the message to all other robots.
guiclient.tk (Tcl/Tk version only): guiclient is a gui based ir client. You can use it to send and recieve bytes with roboroom, and thus all the other robots.
irclient.tcl (Tcl/Tk version only): irclient talks to the real ir tower, so that your emulator robots can talk to the real rcx.
ir-rover.cpp (Tcl/Tk version only): a modified version of simple-rover which will transmit a byte whenever it hits something, all robots in the same room will receive that byte and act as if they have also hit something.
In the Tcl/Tk version you can either set the environment variable RCX_SOURCE to the name of the source file, without the .cpp extension, or modify the target variable in the makefile. This way you can easily build emulators for multiple rcx apps in the same emulegos directory. The name of the generated binary will match the name of the rcx app file.
In the original Windos/C++Builder version the source code must be placed in the "Program" unit. At the beginning of Program.cpp there's a comment that reads:
//---------------------------------------------------------------------------
// WRITE HERE YOUR LEGOS PROGRAM
// - remove all the standard legos #includes (conio.h, unistd.h, ...)
// - rename the main() function rcx_main()
//---------------------------------------------------------------------------
and this is exactly what you have to do. Nothing more.
There is only one big WARNING about your code: as you are inside your own platform and compiler, you're allowed to do many things that you can't actually do with legOS. Keep always the legOS/RCX limits in mind.
Platform specific implementations can cause you problems too if you don't keep focus on them. A simple example: the int type is typically 32 bits on PC while is 16 bits under legOS.
Once your source file (or Program.cpp) is ready and, if you need it, the RealWorld.cpp too, simply compile and run the application.
In the Tcl/Tk version the makefile produces a binary that matches the name of
the rcx app file. To execute your program (and emulegOS) simply run the binary
passing rcx.tk as a parameter.
For example, assuming you have a file simple_rover.cpp
export RCX_SOURCE=simple_rover
make
simple_rover rcx.tk
In another shell you could build light_sensor.cpp
export RCX_SOURCE=light_sensor
make
light_sensor rcx.tk
With the Borland C++Builder version of emulegOS simply make and run the application from the IDE.
When the application runs, a form appears showing your emulated RCX. Before running the code, you need to configure your sensors using the combo box. As in the actual RCX, emulegOS is actually not aware of the (emulated) physical sensor attached to it, but simply reads the raw value. The interface is just a way to make you the user easily interact with your running code. The sensor type can be changed during the code execution too.
Each sensor has a status label that at run time shows if the sensor itself has been made active (with ds_active()) and defined a rotation sensor (with ds_rotation_on()).
The motor status is displayed below the RCX panel. Each motor shows three data:
The (emulated) lcd and the buttons works exactly like in their hardware counterpart. Push the run button and have luck!
The Tcl/Tk version of emulegOS has some optional (but useful) command line parameters:
rcx.tk [-client arg] [-ir] [-host arg] [-port arg] [-sen1 arg] [-sen2 arg]
[-sen3 arg]
This source file contains two functions: RealWorldInit is called once just before your legOS code is started (when you press the Run button); RealWorldExecute is called every 100ms during code execution.
As always some examples are worth a million words:
// Example 1
// Simulating two touch sensors that bound the motor A movement and close
// after 2 seconds of fwd/rev motion respectively
#define M_A 0
#define M_B 1
#define M_C 2
#define S_1 0
#define S_2 1
#define S_3 2
//---------------------------------------------------------------------------
#pragma package(smart_init)
int mot_a_pos; // the virtual position of your moving part
void RealWorldInit(void)
{
  mot_a_pos=0; // initial position is center
}
void RealWorldExecute(void)
{
  MotorDirection motor_a;
  motor_a = emulegOsGetMotorDir(M_A);
  switch (motor_a)
  {
    case fwd:
      mot_a_pos++;
      break;
    case rev:
      mot_a_pos--;
      break;
  }
  if (mot_a_pos>=20) // left bound reached (20 means 2 seconds)
  {
    mot_a_pos = 20; // don't go over
    emulegOsSetSensor(S_1,TOUCH_ON); // close touch sensor
  }
  else
    emulegOsSetSensor(S_1,TOUCH_OFF);
  if (mot_a_pos<=-20) // right bound reached
  {
    mot_a_pos = -20; // don't go over
    emulegOsSetSensor(S_2,TOUCH_ON); // close touch sensor
  }
  else
    emulegOsSetSensor(S_2,TOUCH_OFF);
}
If you were simulating a self-centering assembly, you could simply add a new "case" for the motor status:
  case off:
    mot_a_pos = 0; // center again when motor is off
    break;
A second example:
// Example 2
// Simulating two rotation sensors 1 & 3 attached to motors A & C
#define M_A 0
#define M_B 1
#define M_C 2
#define S_1 0
#define S_2 1
#define S_3 2
//---------------------------------------------------------------------------
#pragma package(smart_init)
void RealWorldInit(void)
{
  // nothing to do here
}
void RealWorldExecute(void)
{
  MotorDirection motor_a, motor_c;
  motor_a = emulegOsGetMotorDir(M_A);
  motor_c = emulegOsGetMotorDir(M_C);
  switch (motor_a)
  {
    case fwd:
      emulegOsRotateSensor (S_1, 3); // 3 is an arbitrary number of
"ticks"
      break;
    case rev:
      emulegOsRotateSensor (S_1, -3);
      break;
  }
  switch (motor_c)
  {
    case fwd:
      emulegOsRotateSensor (S_3, 3);
      break;
    case rev:
      emulegOsRotateSensor (S_3, -3);
      break;
  }
}
(Tcl/Tk version only)
You can have multiple robots interact. Robots can connect to each other though
a IR chat room, roboroom.tcl.
When one transmits that message is sent to the roboroom which then
forwards the message to all other robots. For example, a modified
version of simple-rover called ir-rover (included in the emulegOS release) will
transmit a byte whenever it hits something, all robots in the same room will
receive that byte and act as if they have also hit something.
There are two other IR clients, guiclient, and irclient:
roboroom and the clients allow some command line parameters:
roboroom.tcl [-port arg] [-echo]
guiclient.tk [-client arg] [-host arg] [-port arg]
irclient.tcl [-client arg] [-host arg] [-port arg] [-com arg]
[-keepAliveByte arg] [-keepAliveMs arg] [-echo]
This is just a list. Please refer to legOS documentation for more info. Other details and useful public constants can be found in the emulegOS.h file.
// Motor functions
// ---------------
void motor_a_dir(MotorDirection dir);
void motor_b_dir(MotorDirection dir);
void motor_c_dir(MotorDirection dir);
void motor_a_speed(unsigned char speed);
void motor_b_speed(unsigned char speed);
void motor_c_speed(unsigned char speed);
void dm_init(void);
void dm_shutdown(void);
// Sensor functions
// ----------------
void ds_active(volatile unsigned* const sens);
void ds_passive(volatile unsigned* const sens);
void ds_rotation_set(volatile unsigned* const sens,int pos);
void ds_rotation_on(volatile unsigned* const sens);
void ds_rotation_off(volatile unsigned* const sens);
void ds_init(void);
void ds_shutdown(void);
// Button functions
// ----------------
int button_state(void);
// Lcd functions
// -------------
void cputs(string s);
void cputw(unsigned word);
(Tcl/Tk version only)
void lcd_int(int i);
void lcd_clear(void);
void lcd_refresh(void);
// IR functions
//---------------
size_t dir_write(void* const buf,size_t len);
(Tcl/Tk version only)
size_t dir_read(void* buf,size_t len);
(Tcl/Tk version only)
void dir_fflush(void);
(Tcl/Tk version only)
// System functions
// ----------------
unsigned int sleep(unsigned int sec);
unsigned int msleep(unsigned int msec);
void delay(unsigned ms);
// Time functions
// --------------
time_t sys_time_ ();
// Task management function
// ----------------
pid_t execi (int (*code_start)(void), priority_t priority, size_t stack_size);
void tm_start (void);
void kill(pid_t pid);
wakeup_t wait_event(wakeup_t (*wakeup)(wakeup_t),wakeup_t data);
Functions to be used inside the RealWorld code to read the status of the motors or change that of the sensors:
// Get motor direction (motor 0=A, 1=B, 2=C)
MotorDirection emulegOsGetMotorDir( int motor );
// Get motor speed (motor 0=A, 1=B, 2=C)
unsigned char emulegOsGetMotorSpeed( int motor );
// Set sensor "_sensor" to value "value" (_sensor in the range 0..2)
void emulegOsSetSensor ( int _sensor, unsigned value);
// Rotate sensor "_sensor" of "rots" ticks (_sensor in the range 0..2)
void emulegOsRotateSensor ( int _sensor, int rots);