Porting a Library

14 May 2010

Hi:

Is there any documentation available to help with this?  I am trying to migrate libraries that I use on my Arduino.

Without knowing what the compiler is expecting, it's a challenge.

I presume I need to change all of my command references to the commands that are listed in the modules in the mbed folder within the compiler.  Is there any help available for this?

Thanks

Tim

15 May 2010

Hi Tim,

There isn't one, but it does seem like a good idea as i'm sure there are a set of common things that need to be done. It'll be an ideal page for the new cookbook we're working on in fact :)

Perhaps we can use this thread to work through the effort for this particular case, and it'll highlight the sort of differences and background learning required to do it. Can you fire away with some code snippits you want help with?

Simon

15 May 2010 . Edited: 18 May 2010

Hi Simon:

This would be great to work through and I would really like to do that.

I think the first roadblock I have is that I am trying to migrate a .h and .cpp file.

The biggest issue is that the library files are obviously different and I don't know the names of the library files for the mbed.

The Arduino has a file called Wire.h which handles the I2C part which is what I am trying to use to get to an LCD display. There is also a Serial.h and SoftSerial.h where SoftSerial allows you to designate which pins you want to use for com.

Obviously there are other .h files for strings and so forth.  I have enclosed the .cpp, .h and my code from a working example where I I have the Aduino interfaced to an I2C display from MatrixOrbital and an RFID reader module.

Please let me know how I can help out with this.  I would like to be involved and help out as much as I can.

Here is the test code for the LCD. (note that not all of the functions have been implemented for the LCD in question.

Note from Tim - I wiped out all of the code that was posted as it is pure Arduino code and just represents alot of useless text to skip over

 

16 May 2010 . Edited: 18 May 2010

Hi Simon:

I found the .h files on the site which is a big help

Tim

16 May 2010

This code appears to use an I2C port using an interface called "Wire" and a Serial interface called "SoftwareSerial". The equivalents for the mbed libraries would be "I2C" and "Serial". So for example, instead of:

SoftwareSerial rfid = SoftwareSerial( rxPin, txPin );
you'd do:

Serial rfid(rxPin, txPin);
Wire seems to be some sort of global, but for mbed you'd just make an I2C object in the same way as Serial.

To be honest, the best approach is probably try to port/rewrite each of the components separately first (LCD and RFID) using the code as a logic reference rather than implementation; this way you'll understand much more what is going on, and have some much neater implementations as a result. As an example of how it could work for the LCD, you might find this (somewhat old) page interesting/inspirational:

Other notes might be:

  • Use single printf() functions as a much more powerful and standard way to print things (rather than the various custom print(value,type), printstr(), println() functions)
  • In most cases where it is using "uint8_t", simply using "int" would be much more appropriate and efficient.
  • There is probably code that could be simplified; things like the "Ascii Conversion" sound very much like they are doing what existing C library functions like sscanf("%x") already do
  • delay() is going to be one of wait(), wait_ms(), wait_us()

So I'd recommend the approach of trying to build the components first, simplifying where possible, and using the code more asa reference than something to "modify". Then building the app should be nice and easy!

Simon

16 May 2010

Hi Simon:

Thanks for your note.  I absolutely agree.  I spent a good part of today tracing through the Arduino library files and the linkages went on and on and on as each .h file #include(d) several others.  At the end of it all I probably had more than 20 .h files in the list and of course it won't compile because it is completely different hardware.

I had decided to scrap the idea when I realized it really involved porting "Wiring" which is the code structure the Arduino is based on and I am not up for that.

I am looking for general information on the available commands such as your example of "wait()" rather than delay().  Is that a function of the particular compiler in use for the mbed and is there a list of commands that the libraries support anywhere?

I appreciate your time to review what I had posted.  You have certainly set me off in the right direction.

Maybe I should search the internet for C library functions as a reference?

Thanks

Tim

18 May 2010 . Edited: 18 May 2010

Hi Simon:

I am posting new code.  This code compiles and loads without error.  I don't seem to have a working print statement to talk to the LCD, but exporting the hex codes for the text at least allows me to see it on the LCD.

There are a number of pc.printf statements that really should be going to the LCD, but until I figure out how to get print working to it, I'm stuck.

I'm also stuck on a structural issue.  In the Arduino, there is no "int main()"  I have used this to accomplish what used to be in my

void setup()

As a second structural sticking point, the Arduino uses void loop() for the main program.  I think I should be using "while[1]{}" but I haven't been successful there either.

Once I get this sorted out, I'll build most of what is in my "int main()" into a library.  I have very little experience doing that which is why the code is where it is.

All suggestions welcome

Thanks

Tim

edited to add the code I forgot to copy in

My machine is only holding 1 line in the CTRL c buffer.  I will have to post the code after I figure out what's going on.

// *******************************************************
// *
// *   i2c RFID Demo Unit 
// *    
// *   LCD Library from 4-2-2009  dale@wentztech.com
// *
// *   modified for Matrix Optical MOI-AL-202
// *   5-17-2010 timandjan@rogers.com
// *
// *******************************************************
#include "mbed.h"
#include "RFID.h"
//
DigitalOut led1(LED1);
//DigitalOut led2(LED2);
//DigitalOut led3(LED3);
//DigitalOut led4(LED4);
RFID rfid (p28,p27);
I2C LCD (p9,p10);
Serial pc (USBTX,USBRX);
const int LCD_address = 0x50;  // I2C address of AL202C display
const int LCD_command = 0xFE;  // Sets "command" for display
int LCD_option    = 0x00;      // Used to pass value of display command ie Clear_Display
int LCD_col       = 0x00;      // holder for column value 1 - 16
int LCD_row       = 0x00;      // holder for row value 1 - 2
int i             = 0;         // holder for loop counter
int unlockSeconds = 2;         // part of original door lock program (likely to clear out)
int checksum      = 0;
//
//************************************************************************
// LCD Send Single Character Function                   
//************************************************************************
void LCD_Send_Char(int LCD_data1)
     {
     char cmd[1];
     cmd[0] = LCD_data1;
     LCD.write(LCD_address, cmd, 1); // Send data element
      pc.printf("LCD_data: %X\n\r");
     }
//
//************************************************************************
// LCD Set Cursor Position Function                   
//************************************************************************
void LCD_Set_Cursor(int LCD_command, int LCD_option, int LCD_col, int LCD_row)
     {
     LCD_option = 0x47;
     char cmd[4];
     cmd[0] = LCD_command;           // 0xFE
     cmd[1] = LCD_option;            // 0x47
     cmd[2] = LCD_col;               // Valid = 1 - 20
     cmd[3] = LCD_row;               // Valid = 1 - 2
     LCD.write(LCD_address, cmd, 4); // Send data elements
      pc.printf("LCD_Cursor: %X\n\r", cmd[0], cmd[1], cmd[2], cmd[3]);
     }
//
//************************************************************************
// LCD Clear Display Function                   
//************************************************************************
void LCD_Clear_Display(int LCD_command, int LCD_option)
     {
     LCD_command = 0xFE;             // command indicator
     LCD_option  = 0x58;             // Clear Display
     char cmd[2];
     cmd[0] = LCD_command;           // 0xFE
     cmd[1] = LCD_option;            // 0x58
     LCD.write(LCD_address, cmd, 2); // Send command element
      pc.printf("LCD_Clear: %X\n\r", cmd[1]);
     }
//
//************************************************************************
// LCD Write First Row Init Function                   
//************************************************************************
void LCD_First_Row(int LCD_address)
     {
     char letters[17];
     letters[0]  = 0x44;  //D
     letters[1]  = 0x69;  //i
     letters[2]  = 0x73;  //s
     letters[3]  = 0x70;  //p
     letters[4]  = 0x6C;  //l
     letters[5]  = 0x61;  //a
     letters[6]  = 0x79;  //y
     letters[7]  = 0x2E;  //.
     letters[8]  = 0x2E;  //.
     letters[9]  = 0x2E;  //.
     letters[10] = 0x2E;  //.
     letters[11] = 0x20;  //
     letters[12] = 0x52;  //R
     letters[13] = 0x65;  //e
     letters[14] = 0x61;  //a
     letters[15] = 0x64;  //d
     letters[16] = 0x79;  //y  
     LCD.write(LCD_address, letters, 17); // Send data elements
      pc.printf("Line 1: %X\n\r", letters[0]);
     }
//
//************************************************************************
// LCD Write Second Row Init Function                   
//************************************************************************
void LCD_Second_Row(int LCD_address)
     {
     char letters[17];
     letters[0]  = 0x52;  //R
     letters[1]  = 0x46;  //F
     letters[2]  = 0x49;  //I
     letters[3]  = 0x44;  //D
     letters[4]  = 0x20;  //
     letters[5]  = 0x4D;  //M
     letters[6]  = 0x6F;  //o
     letters[7]  = 0x64;  //d
     letters[8]  = 0x75;  //u
     letters[9]  = 0x6C;  //l
     letters[10] = 0x65;  //e
     letters[11] = 0x20;  //
     letters[12] = 0x52;  //R
     letters[13] = 0x65;  //e
     letters[14] = 0x61;  //a
     letters[15] = 0x64;  //d
     letters[16] = 0x79;  //y   
     LCD.write(LCD_address, letters, 17); // Send data elements
      pc.printf("Line 1: %X\n\r", letters[0]);
     } 
// 
//************************************************************************
// LCD Write First Row2 Init Function                   
//************************************************************************
void LCD_First2_Row(int LCD_address)
     {
     char letters[17];
     letters[0]  = 0x53;  //S
     letters[1]  = 0x63;  //c
     letters[2]  = 0x61;  //a
     letters[3]  = 0x6E;  //n
     letters[4]  = 0x20;  //
     letters[5]  = 0x54;  //T
     letters[6]  = 0x61;  //a
     letters[7]  = 0x67;  //g
     letters[8]  = 0x2E;  //.
     letters[9]  = 0x2E;  //.
     letters[10] = 0x2E;  //.
     letters[11] = 0x2E;  //.
     letters[12] = 0x2E;  //.
     letters[13] = 0x2E;  //.
     letters[14] = 0x2E;  //.
     letters[15] = 0x2E;  //.
     letters[16] = 0x20;  //   
     LCD.write(LCD_address, letters, 17); // Send data elements
      pc.printf("Line 1: %X\n\r", letters[0]);
     }
//
//************************************************************************
// LCD Write Second2 Row Init Function                   
//************************************************************************
void LCD_Blank_Row(int LCD_address)
     {
     char letters[17];
     letters[0]  = 0x20;  //
     letters[1]  = 0x20;  //
     letters[2]  = 0x20;  //
     letters[3]  = 0x20;  //
     letters[4]  = 0x20;  //
     letters[5]  = 0x20;  //
     letters[6]  = 0x20;  //
     letters[7]  = 0x20;  //
     letters[8]  = 0x20;  //
     letters[9]  = 0x20;  //
     letters[10] = 0x20;  //
     letters[11] = 0x20;  //
     letters[12] = 0x20;  //
     letters[13] = 0x20;  //
     letters[14] = 0x20;  //
     letters[15] = 0x20;  //
     letters[16] = 0x20;  //   
     LCD.write(LCD_address, letters, 17); // Send data elements
      pc.printf("Line 1: %X\n\r", letters[0]);
     } 
// 
/** * Fire the relay to activate the strike plate for the configured * number of seconds. */
// not implemented - display only - Tim
void unlock() 
{  
//digitalWrite(ledPin, HIGH);
//digitalWrite(strikePlate, HIGH);
//delay(unlockSeconds * 1000);
//digitalWrite(strikePlate, LOW);
//digitalWrite(ledPin, LOW);
}
// The tag database consists of two parts. The first part is an array of
// tag values with each tag taking up 5 bytes. The second is a list of
// names with one name for each tag (ie: group of 5 bytes).
char* allowedTags[] = 
  {  
  "3B0035D003",       // Tag 1  
  "3B0034B08C",       // Tag 2  
  "2A005E6EBD",       // Tag 3
  };
// List of names to associate with the matching tag IDs
char* tagName[] = 
  { 
  "Blue Tag  ",       // Tag 1  
  "Yellow Tag",       // Tag 2  
  "Card 28349",       // Tag 3
  };
// Check the number of tags defined
int numberOfTags = sizeof(allowedTags)/sizeof(allowedTags[0]);
int incomingByte = 0; // To store incoming serial data 
//
/** * Search for a specific tag in the database */
int findTag( char tagValue[10] ) 
{  
for (int thisCard = 0; thisCard < numberOfTags; thisCard++) 
{
// Check if the tag value matches this row in the tag database
if(strcmp(tagValue, allowedTags[thisCard]) == 0)    
{
// The row in the database starts at 0, so add 1 to the result so      
// that the card ID starts from 1 instead (0 represents "no match")      
return(thisCard + 1);
}
}
// If we don't find the tag return a tag ID of 0 to show there was no match
return(0);
} 
//    
int main()  
    {
// Init LCD Module
       pc.printf("Clear Screen: \n\r");
      LCD_Clear_Display(LCD_command, LCD_option);
      wait(1);
       pc.printf("Set the Cursor Position for Display Ready \n\r");
// Line1 number1
      LCD_col = 2;
      LCD_row = 1;
      LCD_Set_Cursor(LCD_command, LCD_option, LCD_col, LCD_row);
      wait(1);
       pc.printf("Write Display Ready \n\r");
      wait(1);
      LCD_First_Row(LCD_address);
 // Line2 number1
      LCD_col = 2;
      LCD_row = 2;
      LCD_Set_Cursor(LCD_command, LCD_option, LCD_col, LCD_row);   
      wait(1);
      pc.printf("RFID Module Ready \n\r");
      wait(1);
      LCD_Second_Row(LCD_address);
      wait(2);
      pc.printf("Scan Tag \n\r");
// Clear Display
      LCD_Clear_Display(LCD_command, LCD_option);
// Line1 Number2
      LCD_col = 2;
      LCD_row = 1;
      LCD_Set_Cursor(LCD_command, LCD_option, LCD_col, LCD_row);
      wait(1);
      LCD_First2_Row(LCD_address);
      wait(1);
// Line2 Number2
      LCD_col = 2;
      LCD_row = 2;
      LCD_Set_Cursor(LCD_command, LCD_option, LCD_col, LCD_row);
      wait(1);
      LCD_Blank_Row(LCD_address);
      wait(1);
    } 
//  
// ****************************************************************
void loop()
{
int i         = 0;
int val       = 0;
int checksum  = 0;
int bytesRead = 0;
int tempByte  = 0;
int tagBytes[6];    
// "Unique" tags are only 5 bytes but we need an extra byte for the checksum  
char tagValue[10];  
// Read from the RFID module. Because this connection uses SoftwareSerial  
// there is no equivalent to the Serial.available() function, so at this  
// point the program blocks while waiting for a value from the module  
  if((val = rfid.read()) == 2) 
  {     
// Check for header    
  bytesRead = 0;
   while (bytesRead < 12) 
     {
// Read 10 digit code + 2 digit checksum   
     val = rfid.read();
// Append the first 10 bytes (0 to 9) to the raw tag value
     if (bytesRead < 10)     
       {
       tagValue[bytesRead] = val;
       }
// Check if this is a header or stop byte before the 10 digit reading is complete
     if((val == 0x0D)||(val == 0x0A)||(val == 0x03)||(val == 0x02)) 
         {
         break;                         
// Stop reading
         }      
// Ascii/Hex conversion:      
     if ((val >= '0') && (val <= '9')) 
     {
     val = val - '0';
     }
     else if ((val >= 'A') && (val <= 'F')) 
      {
      val = 10 + val - 'A';
      }
// Every two hex-digits, add a byte to the code:
     if (bytesRead & 1 == 1) 
     {
// Make space for this hex-digit by shifting the previous digit 4 bits to the left
     tagBytes[bytesRead >> 1] = (val | (tempByte << 4));
     if (bytesRead >> 1 != 5) 
       {
// If we're at the checksum byte,
     checksum ^= tagBytes[bytesRead >> 1];
// Calculate the checksum... (XOR)
       };
} else {
  tempByte = val;
  // Store the first hex digit first
  };
  bytesRead++;
  // Ready to read next digit
  }
  // Send the result to the host connected via USB
  if (bytesRead == 12) {
    // 12 digit read is complete
//fix    tagValue[10] = '\0';
    // Null-terminate the string
    LCD_col = 1;
    LCD_row = 1;
    LCD_Set_Cursor(LCD_command, LCD_option, LCD_col, LCD_row);
    pc.printf("Tag read: ");
    for (i=0; i<5; i++) {
      // Add a leading 0 to pad out values below 16
      if (tagBytes[i] < 16) {
        pc.printf("0");
        }
        pc.printf("TagBytes %X\n\r", tagBytes[i]);
      }
      pc.printf("Checksum: ");
      pc.printf("TagBytes %X\n\r",tagBytes[5]);
//      Serial.println(tagBytes[5] == checksum ? " -- passed." : " -- error.");
// Show the raw tag value//
      pc.printf("VALUE: %X\n\r", tagValue);
// Search the tag database for this particular tag
int tagId = findTag( tagValue );
// Only fire the strike plate if this tag was found in the database
if( tagId > 0 )
{
LCD_col = 1;
LCD_row = 2;
LCD_Set_Cursor(LCD_command, LCD_option, LCD_col, LCD_row);   
pc.printf("Granted:  ");
//pc.printf("Tag ID: %X\n\r", (tagName[tagID - 1]));
// Get the name for this tag from the database
unlock();
// Fire the strike plate to open the lock
} else 
{
  LCD_col = 1;
  LCD_row = 2;
  LCD_Set_Cursor(LCD_command, LCD_option, LCD_col, LCD_row);
  pc.printf("Denied :            ");
}
  }
  bytesRead = 0;
  }
}



 

18 May 2010 . Edited: 18 May 2010

Hi Tim,

These setup() and loop() functions are just normal functions, and there is simply some extra hidden code in the arduino environment that is calling them that you don't see. So the equivalent C basically looks something like:

#include "mbed.h"

void setup() { ...code... }

void loop() { ...code.. }

int main() {
    setup();
    while(1) {
        loop();
    }
}

Hope that helps get it running...

Simon

18 May 2010

Hi Simon,

Big help!  Much appreciated!

Can you suggest a good source for understanding how to build a proper library?

Thanks

Tim

20 May 2010

Hi Tim,

I've just written the start of a how to write a library tutorial as a test for our new cookbook. Dan will be putting the beta live soon so perhaps you may want to help us test it and comment on what i've put up already?

This is also an area we aim to improve support for in the compiler/website too once the cookbook is live, so i'll have some more ideas/info on this area in time and would appreciate feedback at that time.

Simon

20 May 2010 . Edited: 20 May 2010

Hi Simon,

I would really like to help test it.  I have written up and tested all of the routines for controlling my i2c LCD in anticipation of building a library of them.  I haven't done the "create your own character parts", but I will include what is needed for displaying custom characters and also creating bar graphs which are supported by the display.  I'm sure the code can be condensed, but that can happen later.  Right now they are all sitting in main.cpp ahead of the main() tag.

I'm also trying to put together a routine for reading an RFID tag.  I'm having problems with it and with Serial.h.  It's a fairly comprehensive routine from a book by Jonathan Oxer and Hugh Blemings called Practical Arduino so it's written for "Wiring" and not for "C", which is why it is giving me trouble.  It all worked exactly as written on the Arduino.  I refuse to give up as there is much to learn from the exercise.  The biggest challenge is understanding exactly what the current code is doing and then of course figuring out what that would look like in "C".

I will watch for a post here to say the tutorial is up for testing to build the library.

Thanks

Tim

edit to add further comment.

More examples would be really helpful.  In the stuff below +mbed in the compiler directory I can see the summary of the variables and commands, but an example of the minimum to send something out the serial link doesn't help me for reading bytes in.

Also, I don't see any reference material for commands on their own.  For example, the standard stuff like scanf and printf.  If you don't know what module a command is in how do you know what commands are available and how to use them?

Thanks

Tim

21 May 2010

Hi Tim,

If you take a look at the mbed.h header file [http://mbed.org/projects/libraries/svn/mbed/trunk/mbed.h], you'll see some of the "Useful C libraries" that are included via their header files; one of these is stdio.h that contains the definitions for I/O functions like scanf and printf.

You can find a brief overview about all the functions it contains and their use here: http://en.wikipedia.org/wiki/Stdio

Hope this helps to better understand the functions available.

Aaron

21 May 2010 . Edited: 21 May 2010

Hi Aaron:

Thank you.  Your post is very helpful.  I had looked at the wiki info for something else and didn't find it very helpful.  Your link to Stdio showed me that there is far more information there than I had realized.

Much appreciated!!!

Tim

Edited to add the following.

I did some searching on the notebook and cookbook pages and I did not find a link anywhere to the wiki site you posted for me.  I also didn't see any effect in clicking on wiki from the main page.  It just called up the cookbook.

I have created a page in my notebook with the link.

 

Tim