EV3 C-code for third party devices (I2C)

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
topikachu
Posts: 2
Joined: 28 Oct 2013, 14:47

Re: EV3 C-code for third party devices (I2C)

Post by topikachu »

I create a pure c application to test my mindsensor psp controller
https://gist.github.com/topikachu/7319065

It only works after set correct DEVCON, and dcm pin mode must be 'F'

If I skip any step or set another pin mode, I only get garbage data from IIC

Does anybody know what the 'F' mode is?
lvoc
Posts: 38
Joined: 10 Sep 2013, 13:34

Re: EV3 C-code for third party devices (I2C)

Post by lvoc »

Besides the sample code, there are three additional files that you will need (lms2012.h, bytecodes.h and lmstypes.h) they are all part of the EV3 kernel source code. There is a short explanation about this at:
http://www.robotnav.com/motors-and-sensors/

Regarding the functional code that I wrote, the port number is defined as the constant XGL_PORT, which corresponds to the port number shown in the Ev3 brick label minus one. The reason why I chose not to pass it as an argument, was because this value is used by two function, one that initializes the port/sensor (init_xg1300l_gyro ) and the function that assembles the gyro/accelerometer values (get_xg1300l_gyro). So, for this simple example, making it global guarantees that it is available (and constant) for both functions.

It is possible to pass the port number as an argument, in that case it must done in the initialization routine. For example, init_xg1300l_gyro(int port_number). Then, this port_number value should be used to initialize a global variable XGL_PORT (which should not be a constant anymore), such that it can be used latter by the get_xg1300l_gyro function.
Last edited by lvoc on 20 Nov 2013, 15:13, edited 1 time in total.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: EV3 C-code for third party devices (I2C)

Post by HaWe »

Thx for your reply!

sorry, tis effing lame forum server drives me crazy.... :evil:
Last edited by HaWe on 05 Nov 2013, 21:02, edited 1 time in total.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: EV3 C-code for third party devices (I2C)

Post by HaWe »

Thx for your reply!

ad 1)
how can I download the 3 .h files (I can't copy and paste them strangely)?
could you - maybe - upload them as a zip attachement here in the forum ?


ad 2)
Having global variables for a sensor is not always a good idea iMO.
both port addresses and i2c device adresses should be always local for the polling function.

Imagine I have several sensors of the same sensor type at different ports - e.g., 1 gyro to control the chassis at port 0x0 and 1 gyro to control the postion of the claw arm at port 0x1.

In this case I would have to initialize both ports to be i2c ports and then may call independently even asynchronously in different tasks:

get_xg1300l_gyro(0, ...)
get_xg1300l_gyro(1, ...)


In case there are several i2c sensors with different i2c addresses at the same port (e.g., 0x1) and additionally at other ports as well, then I would be able to poll them analogously one after the other:

get_xg1300l_gyro(1, ...)
get_nxt_USSensor(1, ...)
get_MS_compass(1, ...)
get_xg1300l_gyro(0, ...)
get_nxt_USSensor(1, ...)
get_xg1300l_gyro(1, ...)
get_nxt_USSensor(2, ...)

In case I have multiples of the same sensor by adjustable i2c device addresses this could look like:

get_iic_custom(1, 0x12, ...)
get_iic_custom(1, 0x40, ...)
get_iic_custom(2, 0x12, ...)
get_iic_custom(1, 0x32, ...)
get_iic_custom(3, 0x012, ...)
get_xg1300l_gyro(1, ...)
get_MS_compass(1, ...)
get_xg1300l_gyro(1, ...)
get_nxt_USSensor(2, ...)
get_iic_custom(1, 0x40, ...)
get_iic_custom(1, 0x12, ...)
lvoc
Posts: 38
Joined: 10 Sep 2013, 13:34

Re: EV3 C-code for third party devices (I2C)

Post by lvoc »

The files should be attached in this message. Just in case, when you click on the github links, your browser will show you the file content (html). In order to download the actual file, you can click on the “Raw” button located just above the first code line on the right. This should show you the only the code, which you should be able to save locally.

I agree with you on the use of global variables, and understand the limitations of the code that I wrote. That code was meant to be a general guideline, perhaps the basis for something more capable. A function that allows all the flexibility that you describe, will likely require taking care of a few other things as you mention.
Attachments
lmstypes.h
(8.19 KiB) Downloaded 333 times
bytecodes.h
(50.46 KiB) Downloaded 324 times
lms2012.h
(48.56 KiB) Downloaded 334 times
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: EV3 C-code for third party devices (I2C)

Post by HaWe »

Lauro,
thank you for your reply and uploading the files, I'll test this ASAP.

I'm glad that we agree about this local vs. global variable thing.
I appreciate to have your code and that I'll be able to use it. Anyway, it's currently the first sensor API code I may try at all as there is currently no general BCC sensor API available at all!

My suggestions were meant for general purposes of course, as sort of guidelines for syntax patterns to be used with sensors.
Surely chaining up IIC devices of same or different types (possibly up to 127 at 1 single ev3 port) won't make things easier, but that's what the big advantage of chaining IIC devices is actually all about.
So now, in a very early state of an API syntax and pattern design, we might be able take the proper precautions today to be able to utilize the technology without any restriction in future.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: EV3 C-code for third party devices (I2C)

Post by HaWe »

hiya,
I tried to adjust your code for lcd-print as follows (XG1300L attached to S1) -

But I observed there are some odd errors...

Code: Select all

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include "lms2012.h"

#include <unistd.h>
#include <pthread.h>

#include "ev3_constants.h"
#include "ev3_command.h"
#include "ev3_button.h"
#include "ev3_timer.h"
#include "ev3_lcd.h"
#include "ev3_sound.h"
#include "ev3_output.h"

    
 //Runtime constants

 const int MAX_SAMPLES = 100;


 //Global variables and constants used by the sensor handling functions

 const int XGL_PACKET_SIZE = 10; //2(gyyro angle) + 2(gyro rate) + 2(acc x) + 2(acc y) + 2(acc z)
 const char XGL_PORT = 0x0; //The ports are designated as XGL_PORT_NUMBER-1
 int xgl_device_file;

 IIC *pXgl;



 int init_xg1300l_gyro()
 {
    IICDAT IicDat;
    char buf[120];

    //Open the device xgl_device_file
    if((xgl_device_file = open(IIC_DEVICE_NAME, O_RDWR | O_SYNC)) == -1)   {
      sprintf(buf, "Failed to open device");
      LcdText(1, 0, 60, buf);
      return 0;
    }

    pXgl = (IIC*)mmap(0, sizeof(IIC), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, xgl_device_file, 0);

    if (pXgl == MAP_FAILED)  {
      sprintf(buf, "Map failed");
      LcdText(1, 0, 60, buf);
      return 0;
    }
    
    //Setup IIC to read 2 packets
    IicDat.Port = XGL_PORT;
    IicDat.Time = 0;
    IicDat.Repeat = 0;
    IicDat.RdLng = XGL_PACKET_SIZE;
    IicDat.WrLng = 2;
    
    // Set the device I2C address
    IicDat.WrData[0] = 0x01;
    
    // Specify the register that will be read (0x42 = angle)
    IicDat.WrData[1] = 0x42;
    
    // Setup I2C comunication
    ioctl(xgl_device_file,IIC_SETUP,&IicDat);
    sprintf(buf,"Device is ready");
    LcdText(1, 0, 60, buf);
    
    return 1;
 }



 void get_xg1300l_gyro(float *angle, float *rate, float *acc_x, float *acc_y, float *acc_z)
 {
    //Compute angle, angular rate and accelerations
    
    *acc_z = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][0]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][1])/100.0;
    *acc_y = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][2]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][3])/100.0;
    *acc_x = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][4]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][5])/100.0;
    *rate  = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][6]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][7])/100.0;
    *angle = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][8]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][9])/100.0;
 }



 void close_xg1300l_gyro()     //Close the device xgl_device_file
 {
    char buf[120];
    
    sprintf(buf,"Closing device\n");
    LcdText(1, 0, 60, buf);
    
    close(xgl_device_file);
 }



int main()
{
  int   i;
  char  buf[120];
  
  float angle;
  float rate;
  float acc_x;
  float acc_y;
  float acc_z;
  if(!init_xg1300l_gyro())  return -1;
  
  for(i = 0;i<MAX_SAMPLES;i++)   {
    get_xg1300l_gyro(&angle, &rate, &acc_x, &acc_y, &acc_z);
    
    sprintf(buf,"Angle = %0.2f [deg], Rate = %0.2f [deg/sec]", angle, rate);
    LcdText(1, 0, 10, buf);
    
    sprintf(buf,"AccX = %0.2f, AccY = %0.2f,AccZ = %0.2f [g]", acc_x, acc_y, acc_z);
    LcdText(1, 0, 30, buf);
    
    sleep(1);
  }

  close_xg1300l_gyro();
}
In file included from XG1300L.c:12:
ev3_constants.h:52:1: warning: "LCD_WIDTH" redefined
In file included from XG1300L.c:7:
lms2012.h:196:1: warning: this is the location of the previous definition
In file included from XG1300L.c:12:
ev3_constants.h:53:1: warning: "LCD_HEIGHT" redefined
In file included from XG1300L.c:7:
lms2012.h:197:1: warning: this is the location of the previous definition
In file included from XG1300L.c:12:
ev3_constants.h:54:1: warning: "TOPLINE_HEIGHT" redefined
In file included from XG1300L.c:7:
lms2012.h:198:1: warning: this is the location of the previous definition
In file included from XG1300L.c:12:
ev3_constants.h:330:1: warning: "FILENAME_SIZE" redefined
In file included from XG1300L.c:7:
lms2012.h:367:1: warning: this is the location of the previous definition
In file included from XG1300L.c:16:
ev3_lcd.h:52:1: warning: "LCD_BUFFER_SIZE" redefined
In file included from XG1300L.c:7:
lms2012.h:1237:1: warning: this is the location of the previous definition
In file included from XG1300L.c:16:
ev3_lcd.h:54: error: redefinition of typedef 'IMGDATA'
lmstypes.h:69: error: previous declaration of 'IMGDATA' was here
ev3_lcd.h:55: error: redefinition of typedef 'IP'
lmstypes.h:74: error: previous declaration of 'IP' was here
make: *** [XG1300L] Error 1
lvoc
Posts: 38
Joined: 10 Sep 2013, 13:34

Re: EV3 C-code for third party devices (I2C)

Post by lvoc »

I tested your code, and I got the same errors. I found that they were caused because some of the ev3_*.h include files where using the exact same names that LEGO uses in its LMS2012.H include file. I fixed the issues that were causing errors (you will still get many warnings) and I was able to compile and run the program (see screenshot below).

Image

I am attaching the ev3_* files that I modified. I also had to make a couple of modifications in the MAIN.C program, which is shown below:

Code: Select all

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include "lms2012.h"
#include "ev3_lcd.h"
   
//Runtime constants
const int MAX_SAMPLES = 1000;

//Global variables and constants used by the sensor handling functions

const int XGL_PACKET_SIZE = 10; //2(gyyro angle) + 2(gyro rate) + 2(acc x) + 2(acc y) + 2(acc z)
const char XGL_PORT = 0x0; //The ports are designated as XGL_PORT_NUMBER-1
int xgl_device_file;

IIC *pXgl;

int init_xg1300l_gyro()
{
    IICDAT IicDat;
    char buf[120];

    //Open the device xgl_device_file
    if((xgl_device_file = open(IIC_DEVICE_NAME, O_RDWR | O_SYNC)) == -1)   {
      sprintf(buf, "Failed to open device");
      LcdText(1, 0, 60, buf);
      return 0;
    }

    pXgl = (IIC*)mmap(0, sizeof(IIC), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, xgl_device_file, 0);

    if (pXgl == MAP_FAILED)  {
      sprintf(buf, "Map failed");
      LcdText(1, 0, 60, buf);
      return 0;
    }
   
    //Setup IIC to read 2 packets
    IicDat.Port = XGL_PORT;
    IicDat.Time = 0;
    IicDat.Repeat = 0;
    IicDat.RdLng = XGL_PACKET_SIZE;
    IicDat.WrLng = 2;
   
    // Set the device I2C address
    IicDat.WrData[0] = 0x01;
   
    // Specify the register that will be read (0x42 = angle)
    IicDat.WrData[1] = 0x42;
   
    // Setup I2C comunication
    ioctl(xgl_device_file,IIC_SETUP,&IicDat);
    sprintf(buf,"Device is ready");
    LcdText(1, 0, 60, buf);
   
    return 1;
}

void get_xg1300l_gyro(float *angle, float *rate, float *acc_x, float *acc_y, float *acc_z)
{
    //Compute angle, angular rate and accelerations
   
    *acc_z = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][0]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][1])/100.0;
    *acc_y = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][2]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][3])/100.0;
    *acc_x = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][4]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][5])/100.0;
    *rate  = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][6]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][7])/100.0;
    *angle = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][8]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][9])/100.0;
}

void close_xg1300l_gyro()     //Close the device xgl_device_file
{
    char buf[120];
   
    sprintf(buf,"Closing device\n");
    LcdText(1, 0, 60, buf);
   
    close(xgl_device_file);
}

int main()
{
  int   i;
  char  buf[120];
 
  float angle;
  float rate;
  float acc_x;
  float acc_y;
  float acc_z;  
	
	LcdInit();
	LcdClean();

  if(!init_xg1300l_gyro()) 
		return -1;

	LcdClean();

  for(i = 0;i<MAX_SAMPLES;i++)   {
    get_xg1300l_gyro(&angle, &rate, &acc_x, &acc_y, &acc_z);
   
    sprintf(buf,"Angle = %0.2f [deg]", angle);
    LcdText(1, 0, 10, buf);
    sprintf(buf,"Rate = %0.2f [deg/sec]", rate);
    LcdText(1, 0, 30, buf);
    sprintf(buf,"AccX = %0.2f [g]", acc_x);
    LcdText(1, 0, 50, buf);
    sprintf(buf,"AccY = %0.2f [g]", acc_y);
    LcdText(1, 0, 70, buf);
    sprintf(buf,"AccZ = %0.2f [g]", acc_z);
    LcdText(1, 0, 90, buf);
    sleep(1);
  }

  close_xg1300l_gyro();
	LcdExit();
	return 1;
}
Attachments
ev3_lcd.h
Changes: IP -> BRICXCC_IP , IMGDATA -> BRICXCC_IMGDATA
(12.31 KiB) Downloaded 345 times
ev3_lcd.c
Changes: IP -> BRICXCC_IP , IMGDATA -> BRICXCC_IMGDATA
(43.59 KiB) Downloaded 315 times
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: EV3 C-code for third party devices (I2C)

Post by HaWe »

update:

it now works really fine, thank you very much!

I now tried to change your for() loop in to an endless while() loop to exit by a btn press.
At the 1st time I forgot to init the btns, but now they work, nevertheless the brick does not re-start the VM menu after program abortion,
it shows a mindstorms screen but no menu, and green leds are blinking, and no reaction to either button.

What's possibly wrong with my altered code?

Code: Select all

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include "lms2012.h"
#include "ev3_lcd.h"

#include <pthread.h>

#include "ev3_constants.h"
#include "ev3_command.h"
#include "ev3_button.h"
#include "ev3_timer.h"
#include "ev3_sound.h"
#include "ev3_output.h"

//Runtime constants
const int MAX_SAMPLES = 1000;

//Global variables and constants used by the sensor handling functions

const int XGL_PACKET_SIZE = 10; //2(gyyro angle) + 2(gyro rate) + 2(acc x) + 2(acc y) + 2(acc z)
const char XGL_PORT = 0x0; //The ports are designated as XGL_PORT_NUMBER-1
int xgl_device_file;

IIC *pXgl;

int init_xg1300l_gyro()
{
    IICDAT IicDat;
    char buf[120];

    //Open the device xgl_device_file
    if((xgl_device_file = open(IIC_DEVICE_NAME, O_RDWR | O_SYNC)) == -1)   {
      sprintf(buf, "Failed to open device");
      LcdText(1, 0, 110, buf);
      return 0;
    }

    pXgl = (IIC*)mmap(0, sizeof(IIC), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, xgl_device_file, 0);

    if (pXgl == MAP_FAILED)  {
      sprintf(buf, "Map failed");
      LcdText(1, 0, 110, buf);
      return 0;
    }

    //Setup IIC to read 2 packets
    IicDat.Port = XGL_PORT;
    IicDat.Time = 0;
    IicDat.Repeat = 0;
    IicDat.RdLng = XGL_PACKET_SIZE;
    IicDat.WrLng = 2;

    // Set the device I2C address
    IicDat.WrData[0] = 0x01;

    // Specify the register that will be read (0x42 = angle)
    IicDat.WrData[1] = 0x42;

    // Setup I2C comunication
    ioctl(xgl_device_file,IIC_SETUP,&IicDat);
    sprintf(buf,"Device is ready");
    LcdText(1, 0, 110, buf);

    return 1;
}

void get_xg1300l_gyro(float *angle, float *rate, float *acc_x, float *acc_y, float *acc_z)
{
    //Compute angle, angular rate and accelerations

    *acc_z = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][0]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][1])/100.0;
    *acc_y = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][2]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][3])/100.0;
    *acc_x = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][4]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][5])/100.0;
    *rate  = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][6]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][7])/100.0;
    *angle = (pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][8]*256+pXgl->Raw[XGL_PORT][pXgl->Actual[XGL_PORT]][9])/100.0;
}

void close_xg1300l_gyro()     //Close the device xgl_device_file
{
    char buf[120];

    sprintf(buf,"Closing device\n");
    LcdText(1, 0, 110, buf);

    close(xgl_device_file);
}

int main()
{
  int   i;
  char  buf[120];

  float angle;
  float rate;
  float acc_x;
  float acc_y;
  float acc_z;

  LcdInit();
  LcdClean();
  OutputInit();
  ButtonLedInit();

  if(!init_xg1300l_gyro())
      return -1;

  LcdClean();

  while(1)   {
    get_xg1300l_gyro(&angle, &rate, &acc_x, &acc_y, &acc_z);

    sprintf(buf,"Angle = %0.2f [deg]", angle);
    LcdText(1, 0, 10, buf);
    sprintf(buf,"Rate = %0.2f [deg/sec]", rate);
    LcdText(1, 0, 30, buf);
    sprintf(buf,"AccX = %0.2f [g]", acc_x);
    LcdText(1, 0, 50, buf);
    sprintf(buf,"AccY = %0.2f [g]", acc_y);
    LcdText(1, 0, 70, buf);
    sprintf(buf,"AccZ = %0.2f [g]", acc_z);
    LcdText(1, 0, 90, buf);
    
    sleep(1);
    if ( checkButtons() )  break;
  }

  close_xg1300l_gyro();

  OutputClose();
  OutputExit();
  ButtonLedClose();
  ButtonLedExit();
  LcdExit();
  
  return 1;
}
in this state the program also can't be restarted again by BCC via ctrl+F5 or via the BCC explorer tool, the brick is completely hung up, restarting only possibly by removing the batteries.
Last edited by HaWe on 09 Nov 2013, 15:46, edited 1 time in total.
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: EV3 C-code for third party devices (I2C)

Post by HaWe »

are maybe "lms2012.h" and JH's API functions incompatible to each other?
One will have to decide then which functions to use or not.
Or are there even Linux kernel issues for the Lego fw?
In case they really are incompatible or even if there might be kernel issues then one will have to drop and abandon one or the other, possibly even use another Linux distribution like Ralph's instead.

But an easy API with intuitive access also for beginners is inevitable, otherwise there is no other way round for the future than using a VM with a C-based compiler instead...
Post Reply

Who is online

Users browsing this forum: No registered users and 29 guests