(IQ C++) Global pointer causes compilation error

Here’s a simple program written in VEXcode IQ 2.0 which uses dynamic memory allocation:

// Include the IQ Library
#include "iq_cpp.h"

// Allows for easier use of the VEX Library
using namespace vex;

int main() {
  int* ptr;

  ptr = new int(1);
  Brain.Screen.clearScreen();
  Brain.Screen.print("%d ", *ptr);

  delete ptr;
  ptr = new int(2);
  Brain.Screen.print("%d", *ptr);
}

This program compiles fine, and prints the expected result of 1 2 to the brain’s screen. However, if ptr is a global variable instead, then the program fails to compile:

//This program won't compile:
// Include the IQ Library
#include "iq_cpp.h"

// Allows for easier use of the VEX Library
using namespace vex;

int* ptr = NULL;

int main() {
  ptr = new int(1);
  Brain.Screen.clearScreen();
  Brain.Screen.print("%d ", *ptr);

  delete ptr;
  ptr = new int(2);
  Brain.Screen.print("%d", *ptr);
}

The full error message is:

unix build for platform vexiq
CXX src/main.cpp
LINK build/globalptr.elf
build/src/main.o: In function `main': main.cpp:(.text.main+0x6): undefined reference to `
operator new(unsigned int)' main.cpp:(.text.main+0x3e): undefined reference to `operator delete(void*)' main.cpp:(.text.main+0x44): undefined reference to `operator new(unsigned int)'
make: *** [build/globalptr.elf] Error 1
make process closed with exit code : 2

which seems odd to me since clearly it knows about the new and delete operators, as they seem to work fine in the first program.

Globals that aren’t pointers seem to work fine too; for example, the following program compiles and produces the same result:

// Include the IQ Library
#include "iq_cpp.h"

// Allows for easier use of the VEX Library
using namespace vex;

int foo;

int main() {
  foo = 1;
  Brain.Screen.clearScreen();
  Brain.Screen.print("%d ", foo);
  foo = 2;
  Brain.Screen.print("%d", foo);
}

So, what’s up here? Is there some technical limitation that prevents global pointers from working on the IQ brain? or have I just found a bug in the compiler?

4 Likes

There is no dynamic memory allocation supported really.

1 Like

The longer answer is that I think it’s due to C++ trying to make things thread safe. When ptr is local to the function there’s no expectation that another thread would try and access it. When it’s global, the compiler generates a call to std::get_new_handler which is part of the standard C++ library, we do not link against that. Even if we did, IQ is so memory constrained that there is almost no heap, hence my statement that dynamic memory allocation is not supported.

Globals pointers work just fine, just don’t try and use new and delete, they have little use in most IQ programs, just use stack or global memory.

6 Likes

wait is not linking against stdlib just an IQ thing or am I forgetting several things?

Well, this thread is about IQ, not sure what you mean.

IQ has 128k of flash. To have 4 user programs the flash is divided into 4 regions so each program has at most 32k for the program and read only data. Others have chosen to divide that a little differently, the ROBOTC VM was about 80k, so it used all 128k of memory and then implemented its own file system in the remainder. RobotMesh chooses to use 2, 64k slots I think.

Most IQ programs are about 10k in size, using something like std::cout and linking against stdc++ will increase program size to over 75k, never going to fit. Even using the “nano” version of the library and something like std::cout blows past the 32k limit. And then there’s RAM, we have 12k total, and 90% of that is used for system related purposes, the stack for each task etc. so while you can write programs to control the robot using the VEX classes, math functions, etc. you will never have a good experience trying to write generic C++ programs using the standard library, dynamic memory allocation etc. If you want to do that, move on to V5.

8 Likes

I was just under the impression that IQ and V5 were a lot more similar under the hood.

1 Like

The C++ classes are very similar.
but V5 has 72MB for the user program and variables. IQ has 44k (32+12). So V5 has three order of magnitude more memory and a much faster processor.

5 Likes

I’ve been wondering, is there any reason to use vexcode iq over robotc?

I know the file size is pretty obvious. I think robotc allowed you to have around 8 programs on the bot (not very much use for that though), and vexcode allows for just four, so individual file size will be much more accommodating.

Other than that though, is there any reason?

2 Likes

Cross-platform and actively maintained are the big reasons.

ROBOTC is Windows only. VEXcode IQ is Windows, macOS, ChromeOS, iPadOS, Android, Amazon Fire.

ROBOTC isn’t under active development. VEXcode IQ is.

5 Likes

thats not how it works- the number of slots is in correlation to the robot brain, not the programming language

Correct me if I’m wrong, but ROBOTC uses a proprietary compiler while VEXcode IQ uses the widely used and well-documented Clang/LLVM, right?

3 Likes

That is correct.

1 Like

In the list of reasons to use VEXcode IQ. There has to be performance increases from moving to a more professional tool.
No offense @tfriez

1 Like

Multiple file projects? I think this was possible in robotc, but I never used it as I didn’t know multiple files was a thing. (I’ve learned a lot in the past few months)

1 Like

I would have thought he main requirements in an educational tool for young children is ease of use, supporting documentation and resources, familiarity etc.
99.9% of IQ programs do not need performance gains.

2 Likes

In a normal thread I wouldn’t list it. But this is a thread about dynamic memory allocation. The people reading it were not the average IQ users by far.

1 Like

Not necessarily, if you mean “professional” as in “used on wide variety of hardware platforms for many more applications” and “has a lot of developers.”

While you would expect that more developers will translate to more mature and highly optimized code, multiple platforms requirement may mean that more work was directed toward popular platforms. Then even if it works on a niche hardware platform - there is no guarantee that performance was tested and optimized.

But if you have a small team dedicated to a specific hardware - they could do very targeted optimizations and make it work well where it matters.

Yeah, but VEXcode IQ is a Scratch-based language aimed at elementary and middle school children that also has a C++ interface. It’s not a professional tool so to expect professional features and performance is not realistic and that is why that would not be cited as a reason to use VEXcode IQ.

1 Like

Potentially since VEXcode is compiling directly to the processor, where as ROBOTC was running a VM architecture (which allowed us to do things like variable monitoring, stepping, etc.) - so VEXcode IQ might have execution speed gains, but ROBOTC has functionality that was afforded by the VM (and other tweaks/adjustments we made, such as using our own file system over slots).

But since the question was related to “reason to use ROBOTC over VEXcode” - the primary advantages at the moment are related to active maintenance/development and broader device support… if you’re happy with ROBOTC on IQ and the limitations there of, there’s no real reason to switch…

(at least until the Windows Update lords release an update that breaks something in ROBOTC…)

6 Likes

I’ve made a few comments regarding the limitations of C++ on IQ, however, I want to emphasize that you can write some pretty large programs using C++, you just need to keep within the constraints of the system and perhaps have a bias towards writing C code rather than C++.

A few years back I ported the (at that time) default IQ drive code to ROBOTC, you can find that in a repo here.

It was a pretty good reproduction of the default drive code with the exception that user programs cannot use the “exit” button on the brain.

When we first were testing C++ on IQ, I ported that project to the new SDK as a test to see where the limit of code size etc. was. that was about 18 months ago using very preliminary tools. Just to see where we ended up I recently brought that into VEXcode IQ, a few minor changes were needed (no dynamic memory, controller needs configuring in the graphical configuration) but it seems to compile and run ok. Here’s the code, it’s about 1500 lines.

(code is too large for one forum post)

code part 1, continued in next post
/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*                        Copyright (c) James Pearman                          */
/*                                   2016                                      */
/*                            All Rights Reserved                              */
/*                                                                             */
/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*    Module:     iqDefaultCode.c                                              */
/*    Author:     James Pearman                                                */
/*    Created:    31 Jan 2016                                                  */
/*                                                                             */
/*                V1.00 31 Jan 2016  Initial release tested with V4.52         */
/*                                                                             */
/*    This version for VEXcode IQ 2.0.1, August 5 2020                         */
/*                                                                             */
/*                                                                             */
/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*    The author is supplying this software for use with the VEX IQ            */
/*    control system. This file can be freely distributed and teams are        */
/*    authorized to freely use this program , however, it is requested that    */
/*    improvements or additions be shared with the Vex community via the vex   */
/*    forum.  Please acknowledge the work of the authors when appropriate.     */
/*    Thanks.                                                                  */
/*                                                                             */
/*    Licensed under the Apache License, Version 2.0 (the "License");          */
/*    you may not use this file except in compliance with the License.         */
/*    You may obtain a copy of the License at                                  */
/*                                                                             */
/*      http://www.apache.org/licenses/LICENSE-2.0                             */
/*                                                                             */
/*    Unless required by applicable law or agreed to in writing, software      */
/*    distributed under the License is distributed on an "AS IS" BASIS,        */
/*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
/*    See the License for the specific language governing permissions and      */
/*    limitations under the License.                                           */
/*                                                                             */
/*    The author can be contacted on the vex forums as jpearman                */
/*    or electronic mail using jbpearman_at_mac_dot_com                        */
/*    Mentor for team 8888 RoboLancers, Pasadena CA.                           */
/*                                                                             */
/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*    Description:                                                             */
/*                                                                             */
/*    A simulation of the Driver Control program for VEX IQ written in ROBOTC. */
/*                                                                             */
/*    PORT1  Left Drive Motor (optional)                                       */
/*    PORT2  Bumper switch, stop PORT4 motor going reverse. Or any sensor.     */
/*    PORT3  Bumper switch, stop PORT4 motor going forward. Or any sensor.     */
/*    PORT4  Auxilary motor (BtnRUp & BtnRDown). Or any sensor.                */
/*    PORT5  Auxilary motor (BtnFUp & BtnFDown). Or any sensor.                */
/*    PORT6  Right Drive Motor (optional)                                      */
/*                                                                             */
/*    PORT7  Left Drive Motor (optional)                                       */
/*    PORT8  Bumper switch, stop PORT10 motor going reverse. Or any sensor.    */
/*    PORT9  Bumper switch, stop PORT10 motor going forward. Or any sensor.    */
/*    PORT10 Auxilary motor (BtnLUp & BtnLDown). Or any sensor.                */
/*    PORT11 Auxilary motor (BtnEUp & BtnEDown. Or any sensor.)                */
/*    PORT12 Right Drive Motor (optional)                                      */
/*                                                                             */
/*    Optional sensors                                                         */
/*      Touch LED, enable/disable robot, any free port                         */
/*      Color Sensor, enable/disable robot, any free port                      */
/*      Gyro, reset robot to start orientation, any free port                  */
/*      Sonar (distance), stop robot hitting objects, any free port            */
/*                                                                             */
/*-----------------------------------------------------------------------------*/
#include "iq_cpp.h" 

using namespace vex;

// configuration menu items
typedef enum tConfigSelect {
    kConfig_exit = 0,
    kConfig_0    = 1,
    kConfig_1    = 2,
    kConfig_2    = 3,
    kConfig_3    = 4,
    kConfig_4    = 5,
    kConfig_5    = 6,
    kConfig_6    = 7,
    kConfig_7    = 8
} tConfigSelect;

// joystick control modes
typedef enum tJoystickMode {
    kJoystickTank,
    kJoystickArcadeLeft,
    kJoystickArcadeRight
} tJoystickMode;

// motor control direction
typedef enum tMotorDirection {
    kNormal,
    kReverse
} tMotorDirection;

// Motor data is collected together in a structure
typedef struct _IQMotor {
    bool              installed;    ///< is motor installed
    tMotorDirection   direction;    ///< reverse motor
    vex::bumper      *fwdTouch;     ///< touch sensor port to stop motor going forwards
    vex::bumper      *revTouch;     ///< touch sensor port to stop motor going reverse
    float             velocity;     ///< calculated velocity
    long              lastEncoder;
    long              lastTime;
    vex::motor       &instance;
} IQMotor;

// Default setting for config page if they are not stored
// in memory
#define   DRIVE_MODE_DEFAULT    kJoystickTank
#define   DRIVE_TURN_DEFAULT    kNormal
#define   DRIVE_FWD_DEFAULT     kNormal
#define   MOTOR_04_DEFAULT      kNormal
#define   MOTOR_05_DEFAULT      kNormal
#define   MOTOR_10_DEFAULT      kNormal
#define   MOTOR_11_DEFAULT      kNormal

#define   IQ_TICK_PER_REV       960   ///< raw encoder counts/rev

// Globals to hold state and sensor info
#define PORTNONE  (-1)

// LCD rows
#define LCD_ROW1    1
#define LCD_ROW2    2
#define LCD_ROW3    3
#define LCD_ROW4    4
#define LCD_ROW5    5
#define LCD_ROW6    6
#define LCD_ROW7    7
#define LCD_ROW8    8
#define LCD_ROW9    9

#define LCD_ROW     9

// define in graphical config
//vex::controller Controller;

vex::timer       T1;
vex::motor       Motor1( vex::PORT1 );
vex::motor       Motor6( vex::PORT6 );
vex::motor       Motor7( vex::PORT7 );
vex::motor       Motor12( vex::PORT12 );

vex::motor       Motor4( vex::PORT4 );
vex::motor       Motor5( vex::PORT5 );
vex::motor       Motor10( vex::PORT10 );
vex::motor       Motor11( vex::PORT11 );

vex::motor       Motor2( vex::PORT2 );
vex::motor       Motor3( vex::PORT3 );
vex::motor       Motor8( vex::PORT8 );
vex::motor       Motor9( vex::PORT9 );

vex::gyro        robotGyroSensor(0);
vex::touchled    robotTouchSensor(0);
vex::colorsensor robotColorSensor(0);
vex::sonar       robotSonarSensor(0);

vex::bumper      Bumper2( PORT2 );
vex::bumper      Bumper3( PORT3 );
vex::bumper      Bumper8( PORT8 );
vex::bumper      Bumper9( PORT9 );

// Globals used by the code
tJoystickMode   configDriveMode  = DRIVE_MODE_DEFAULT;
tMotorDirection configDriveTurn  = DRIVE_TURN_DEFAULT;
bool            robotRunning     = false;
bool            robotEnabled     = true;
bool            sonarFront       = true;
IQMotor         IQMotors[ 12 ] = {
  { .instance = Motor1  },
  { .instance = Motor2  },
  { .instance = Motor3  },
  { .instance = Motor4  },
  { .instance = Motor5  },
  { .instance = Motor6  },
  { .instance = Motor7  },
  { .instance = Motor8  },
  { .instance = Motor9  },
  { .instance = Motor10 },
  { .instance = Motor11 },
  { .instance = Motor12 }
};

short           robotTotalDevices;

const unsigned char logo[320] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
0x0C,0x00,0x30,0x7F,0xFE,0x30,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00, 
0x1E,0x00,0x78,0xFF,0xFE,0x3C,0x07,0x80,0x00,0x00,0x00,0x00,0x01,0xE0,0x00,0x00, 
0x1F,0x00,0x78,0xFF,0xFE,0x3C,0x0F,0x00,0x00,0x00,0x00,0x00,0x01,0xE0,0x00,0x00, 
0x0F,0x00,0xF0,0xF0,0x00,0x1E,0x1E,0x00,0x00,0x00,0x00,0x00,0x01,0xE0,0x00,0x00, 
0x0F,0x80,0xF0,0xF0,0x00,0x1F,0x3E,0x00,0xF8,0x00,0xF8,0x00,0xF9,0xE0,0x3F,0x00, 
0x07,0x81,0xE0,0xF0,0x00,0x0F,0xFC,0x07,0xFE,0x07,0xFF,0x03,0xFD,0xE0,0xFF,0xC0, 
0x07,0xC1,0xE0,0xFF,0xF8,0x07,0xF8,0x0F,0x9F,0x0F,0x8F,0x87,0xCF,0xE1,0xF3,0xE0, 
0x03,0xC3,0xC0,0xFF,0xFC,0x03,0xF0,0x1E,0x07,0x8F,0x07,0x8F,0x83,0xE3,0xC1,0xF0, 
0x03,0xC3,0xC0,0xFF,0xFC,0x03,0xF0,0x1E,0x03,0x1E,0x03,0xCF,0x01,0xE3,0xC0,0xF0, 
0x01,0xE7,0x80,0xF0,0x00,0x07,0xF8,0x1E,0x00,0x1E,0x03,0xCF,0x01,0xE3,0xC0,0xF0, 
0x01,0xE7,0x80,0xF0,0x00,0x0F,0xFC,0x1C,0x00,0x1E,0x03,0xCF,0x01,0xE3,0xFF,0xE0, 
0x00,0xF7,0x00,0xF0,0x00,0x1F,0x3E,0x1E,0x03,0x9E,0x03,0xCF,0x01,0xE3,0xC0,0x00, 
0x00,0xFF,0x00,0xF0,0x00,0x3E,0x1F,0x1F,0x07,0x8F,0x07,0x87,0x83,0xE3,0xC0,0x70, 
0x00,0x7E,0x00,0xFF,0xFE,0x7C,0x0F,0x0F,0xFF,0x0F,0xFF,0x87,0xFF,0xE1,0xFF,0xE0, 
0x00,0x7E,0x00,0xFF,0xFF,0x78,0x07,0x87,0xFE,0x03,0xFE,0x03,0xFD,0xE0,0xFF,0xC0, 
0x00,0x18,0x00,0x7F,0xFE,0x30,0x03,0x00,0xF0,0x00,0xF8,0x00,0xF0,0xC0,0x1F,0x00, 
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 
};

const unsigned char *getLogo() {
  return( logo );
}
/*-----------------------------------------------------------------------------*/
/*  Draw the bitmap defined in the array above on the LCD                      */
/*  Just a hardcoded hack for now, will improve later                          */
/*-----------------------------------------------------------------------------*/

void
drawBitmap( const unsigned char *bitmap )
{
    int row, stripe, pix;
    int p;

    Brain.Screen.setPenColor( vex::colorType::white );
    Brain.Screen.setFillColor( vex::colorType::white );
    Brain.Screen.drawRectangle( 0, 10, 127, 24 );

    Brain.Screen.setPenColor( vex::colorType::black );
    // 24 rows
    Brain.Screen.setAspectCompensation(false);
    for(row=0;row<20;row++)
        {
        // 16 bytes each with 8 pixels
        for(stripe=0;stripe<16;stripe++)
            {
            p = bitmap[ (row*16) + stripe ];
            for(pix=0;pix<8;pix++)
                {
                if( ( p & 0x80 ) )
                    Brain.Screen.drawPixel( (stripe*8) + pix, (row+10) );
                p <<= 1;
                }
            }
        }
    Brain.Screen.setAspectCompensation(true);
}

// Base address of EEPROM
unsigned long eepromPtr = 0x400AF000;

#define   EEPROM_PAGE_REG   0x04
#define   EEPROM_OFFSET_REG 0x08
#define   EEPROM_RDWR_REG   0x10
#define   EEPROM_RDWRI_REG  0x14

// The page we use, be very careful with this, don't change it !!
#define   EEPROM_PAGE       0x15

/*-----------------------------------------------------------------------------*/
/*    Initialize read or write                                                 */
/*-----------------------------------------------------------------------------*/
void
iqSettingsSetupPage()
{
    unsigned long *p;

    p = (unsigned long *)(eepromPtr+EEPROM_PAGE_REG);
    *p = EEPROM_PAGE; // default page is 21
    p = (unsigned long *)(eepromPtr+EEPROM_OFFSET_REG);
    *p = 0;           // offset 0
}

/*-----------------------------------------------------------------------------*/
/*    Read one unsigned long from EEPROM                                       */
/*-----------------------------------------------------------------------------*/
unsigned long
iqSettingsReadData()
{
    unsigned long *p;
    unsigned long data;

    iqSettingsSetupPage();
    p = (unsigned long *)(eepromPtr+EEPROM_RDWR_REG);
    data = *p;

    vex::console::write("r %08X\n", data);

    return( data );
}
/*-----------------------------------------------------------------------------*/
/*    Write one unsigned long to EEPROM                                        */
/*-----------------------------------------------------------------------------*/
void
iqSettingsWriteData( unsigned long data )
{
    unsigned long *p;

    iqSettingsSetupPage();
    p = (unsigned long *)(eepromPtr+EEPROM_RDWR_REG);
    *p = data;
    vex::console::write("w %08X\n", data);
}

/*-----------------------------------------------------------------------------*/
/*  Simple integrity check on EEPROM page to check most locations are 0xFF     */
/*-----------------------------------------------------------------------------*/
bool
iqSettingsIntegrityCheck()
{
    volatile unsigned long *p;
    int  i;
    bool fail = false;

    // Do some simple checks on the EEPROM
    iqSettingsSetupPage();

    p = (unsigned long *)(eepromPtr+EEPROM_RDWRI_REG);

    // ignore first location, we may have written bad data
    volatile int tmp1;
    unsigned long tmp = *p; tmp1 = tmp;

    // all other locations should be 0xFF
    for(i=0;i<8;i++) {
      if( *p != 0xFFFFFFFF )
        fail = true;
    }
    return( !fail );
}

/*-----------------------------------------------------------------------------*/
/*  Masks for our config data                                                  */
/*-----------------------------------------------------------------------------*/
#define   CODE_MASK  0xFFFF0000
#define   CODE_IDENT 0xC0DE0000
#define   MODE_MASK  0x00000003
#define   TURN_MASK  0x00000004
#define   M01_MASK   0x00000008
#define   M04_MASK   0x00000010
#define   M05_MASK   0x00000020
#define   M10_MASK   0x00000040
#define   M11_MASK   0x00000080
#define   SONAR_MASK 0x00000100

/*-----------------------------------------------------------------------------*/
/*    Store configuration settings                                             */
/*-----------------------------------------------------------------------------*/
void
iqSettingsStore()
{
    unsigned long data;

    data = iqSettingsReadData();
    
    switch(configDriveMode)
        {
        case  kJoystickTank:
            data = (data & ~MODE_MASK);
            break;
        case  kJoystickArcadeLeft:
            data = (data & ~MODE_MASK) | 0x00000001;
            break;
        case  kJoystickArcadeRight:
            data = (data & ~MODE_MASK) | 0x00000002;
            break;
        }

    if( configDriveTurn == kNormal )
        data &= ~TURN_MASK;
    else
        data |=  TURN_MASK;

    if( IQMotors[PORT1 ].direction == kNormal )
        data &= ~M01_MASK;
    else
        data |=  M01_MASK;

    if( IQMotors[PORT4 ].direction == kNormal )
        data &= ~M04_MASK;
    else
        data |=  M04_MASK;
    if( IQMotors[PORT5 ].direction == kNormal )
        data &= ~M05_MASK;
    else
        data |=  M05_MASK;
    if( IQMotors[PORT10].direction == kNormal )
        data &= ~M10_MASK;
    else
        data |=  M10_MASK;
    if( IQMotors[PORT11].direction == kNormal )
        data &= ~M11_MASK;
    else
        data |=  M11_MASK;
    if( sonarFront )
        data &= ~SONAR_MASK;
    else
        data |=  SONAR_MASK;

    iqSettingsWriteData( data );
}

/*-----------------------------------------------------------------------------*/
/*    Never used so init the EEPROM word                                       */
/*-----------------------------------------------------------------------------*/
void
iqSettingsInit( unsigned long data )
{
    iqSettingsWriteData( data );
}

/*-----------------------------------------------------------------------------*/
/*    Retrieve data word and setup configure options                           */
/*-----------------------------------------------------------------------------*/
void
iqSettingsRetrieve()
{
    unsigned long data;

    data = iqSettingsReadData();

    if( (data & CODE_MASK) == CODE_IDENT ) {
      //writeDebugStreamLine("Load %08X", data );
      switch( data & MODE_MASK )
          {
          case  0:
            configDriveMode = kJoystickTank;
            break;
          case  1:
            configDriveMode = kJoystickArcadeLeft;
            break;
          case  2:
            configDriveMode = kJoystickArcadeRight;
            break;
          }

      configDriveTurn            = (data & TURN_MASK) ? kReverse : kNormal;
      IQMotors[PORT1 ].direction = (data & M01_MASK) ? kReverse : kNormal;
      IQMotors[PORT6 ].direction = (data & M01_MASK) ? kReverse : kNormal;
      IQMotors[PORT4 ].direction = (data & M04_MASK) ? kReverse : kNormal;
      IQMotors[PORT5 ].direction = (data & M05_MASK) ? kReverse : kNormal;
      IQMotors[PORT10].direction = (data & M10_MASK) ? kReverse : kNormal;
      IQMotors[PORT11].direction = (data & M11_MASK) ? kReverse : kNormal;
      sonarFront = (data & SONAR_MASK) ? false : true;
    }
    else {
        if( iqSettingsIntegrityCheck() )
            iqSettingsInit( CODE_IDENT );
    }
}
/*-----------------------------------------------------------------------------*/
/*  Reset configuration and save                                               */
/*-----------------------------------------------------------------------------*/
void
iqResetToDefaults()
{
    configDriveMode  = DRIVE_MODE_DEFAULT;
    configDriveTurn  = DRIVE_TURN_DEFAULT;

    IQMotors[ (short)PORT1  ].direction = DRIVE_FWD_DEFAULT;
    IQMotors[ (short)PORT6  ].direction = DRIVE_FWD_DEFAULT;
    IQMotors[ (short)PORT4  ].direction = MOTOR_04_DEFAULT;
    IQMotors[ (short)PORT5  ].direction = MOTOR_05_DEFAULT;
    IQMotors[ (short)PORT10 ].direction = MOTOR_10_DEFAULT;
    IQMotors[ (short)PORT11 ].direction = MOTOR_11_DEFAULT;

    sonarFront = true;

    iqSettingsStore();

    //playSound(soundTada);
}

/*-----------------------------------------------------------------------------*/
/*  Control robot wheels                                                       */
/*-----------------------------------------------------------------------------*/
void
iqRunRobotDrive()
{
    short leftDrive;
    short rightDrive;

    if( robotEnabled ) {
        switch( configDriveMode )
            {
            case kJoystickTank:
              leftDrive  = Controller.AxisA.value(); 
              rightDrive = Controller.AxisD.value();
              break;
            case kJoystickArcadeLeft:
              leftDrive  = Controller.AxisA.value() + Controller.AxisB.value();
              rightDrive = Controller.AxisA.value() - Controller.AxisB.value();
              break;
            case kJoystickArcadeRight:
              leftDrive  = Controller.AxisD.value() + Controller.AxisC.value();
              rightDrive = Controller.AxisD.value() - Controller.AxisC.value();
              break;
            }

        // threshold
        if( abs(leftDrive)  < 10 ) leftDrive  = 0;
        if( abs(rightDrive) < 10 ) rightDrive = 0;

        // flip drive direction based on configuration
        leftDrive  = (IQMotors[PORT1 ].direction == kNormal) ? leftDrive  : -leftDrive;
        rightDrive = (IQMotors[PORT6 ].direction == kNormal) ? rightDrive : -rightDrive;

        // reverse left and right & flips signs
        // Not sure I agree with this but it makes robot behave
        // as per the default driver program.
        // Robot front is now back.
        if( configDriveTurn == kReverse ) {
            int tmp = leftDrive;
            leftDrive  = -rightDrive;
            rightDrive = -tmp;
        }

        // When moving reset a timer used to start gyro control
        if( leftDrive || rightDrive )
            T1.clear();

        // Gyro sensor
        if( robotGyroSensor.installed() ) {
            // If stopped for more than 500mS then reorientate using gyro
            if( T1.time() >= 500 ) {
                int heading = robotGyroSensor.angle();
                
                if( heading > 180 )
                  heading -= 360;

                // small amount of hysteresis
                if( abs( heading ) < 2 ) {
                    leftDrive = 0;
                    rightDrive = 0;
                }
                else
                if( heading > 3 ) {
                    leftDrive  =  30 + heading / 2;
                    rightDrive = -30 - heading / 2;
                }
                else
                if( heading < -3 ) {
                    leftDrive  = -30 + heading / 2;
                    rightDrive =  30 - heading / 2;
                }
            }
        }

        // Sonar sensor installed
        // stop motors going forwards
        if( robotSonarSensor.installed() ) {
          // stop at 15cm
          if( robotSonarSensor.distance(distanceUnits::mm ) < 150 )
            {
            if( (sonarFront && leftDrive > 0) || (!sonarFront && leftDrive < 0) )
              leftDrive = 0;
            if( (sonarFront && rightDrive > 0) || (!sonarFront && rightDrive < 0) )
              rightDrive = 0;
            }
        }
    }
    else {
        leftDrive  = 0;
        rightDrive = 0;
    }

    // Control drive motors.
    if( IQMotors[ PORT1 ].installed )
      Motor1.spin( fwd, leftDrive, rpm );
    if( IQMotors[ PORT7 ].installed )
      Motor7.spin( fwd, leftDrive, rpm );
    if( IQMotors[ PORT6 ].installed )
      Motor6.spin( fwd, -rightDrive, rpm );  // right is reversed
    if( IQMotors[ PORT12 ].installed )
      Motor12.spin( fwd, -rightDrive, rpm ); // right is reversed
}
/*-----------------------------------------------------------------------------*/
/*  Control the four Auxiliary motors, ports 4, 5, 10 & 11                     */
/*-----------------------------------------------------------------------------*/
void
iqRunRobotAux()
{
    int m04Drive;
    int m05Drive;
    int m10Drive;
    int m11Drive;

    if( robotEnabled ) {
        // Control the auxilary motors
        m04Drive = ( Controller.ButtonRUp.pressing() * 100) - (Controller.ButtonRDown.pressing() * 100);
        m05Drive = ( Controller.ButtonFUp.pressing() * 100) - (Controller.ButtonFDown.pressing() * 100);
        m10Drive = ( Controller.ButtonLUp.pressing() * 100) - (Controller.ButtonLDown.pressing() * 100);
        m11Drive = ( Controller.ButtonEUp.pressing() * 100) - (Controller.ButtonEDown.pressing() * 100);

        m04Drive = (IQMotors[PORT4 ].direction == kNormal) ? m04Drive : -m04Drive;
        m05Drive = (IQMotors[PORT5 ].direction == kNormal) ? m05Drive : -m05Drive;
        m10Drive = (IQMotors[PORT10].direction == kNormal) ? m10Drive : -m10Drive;
        m11Drive = (IQMotors[PORT11].direction == kNormal) ? m11Drive : -m11Drive;

        // Deal with touch sensors
        // stopping motor 4 and motor 10
        if( IQMotors[ PORT4 ].fwdTouch != NULL ) {
            if( m04Drive > 0 && IQMotors[ PORT4 ].fwdTouch->pressing() )
                m04Drive = 0;
        }
        if( IQMotors[ PORT4 ].revTouch != NULL ) {
            if( m04Drive < 0 && IQMotors[ PORT4 ].revTouch->pressing() )
                m04Drive = 0;
        }
        if( IQMotors[ PORT10 ].fwdTouch != NULL ) {
            if( m10Drive > 0 && IQMotors[ PORT10 ].fwdTouch->pressing() )
                m10Drive = 0;
        }
        if( IQMotors[ PORT10 ].revTouch != NULL) {
            if( m10Drive < 0 && IQMotors[ PORT10 ].revTouch->pressing() )
                m10Drive = 0;
        }
    } // robotEnabled
    else {
        // Robot disabled
        m04Drive = m05Drive = m10Drive = m11Drive = 0;
    }

    // Control Aux motors
    if(IQMotors[ PORT4 ].installed)
      Motor4.spin( fwd, m04Drive, rpm );
    if(IQMotors[ PORT5 ].installed)
      Motor5.spin( fwd, m05Drive, rpm );
    if(IQMotors[ PORT10 ].installed)
      Motor10.spin( fwd, m10Drive, rpm );
    if(IQMotors[ PORT11 ].installed)
      Motor11.spin( fwd, m11Drive, rpm );
}

As the code has been ported a couple of times, it’s not necessarily how I would write it from scratch now, it had to work with ROBOTC limitations originally. But I wanted to show that IQ can support more than just trivial code. The stats for this program are

   text	   data	    bss	    dec	    hex	filename
  28460	    536	   9900	  38896	   97f0	iqDefault.elf

so about 28k of code and still a reasonable amount of RAM remaining (around 2k) primarily because no events are being used. (installing even one event will use another 1.5k of memory, it’s been pulled out due to optimization by the linker)

3 Likes