Custom Controller?

Wow thanks, This is great. It provides a great example of how to code for the Cortex serial port handler as well. I had something working based upon the Fire project, but this is a better example.

It’s a weird coincidence as I am currently camping in the Rocky mountains, while my wife is attending the Planet Bluegrass ‘Song School’ I’m in the campground working on ‘geek’ robotic projects. It keeps me busy till the jam sessions in the evening, then off to play another way. Thanks again and I hope to have something in return by this weekend.

Cheers Kb

James,
Do you know if Robot C supports the ‘union’ capability? it doesn’t appear to from my attempts. It sure looks like a great way to access the data.

Cheers Kb

It is, but it’s a bit messed up. For example, here is the union from the example ported to ROBOTC with some test code.

// Storage for the vex communications data
#define  VEXDATAOFFSET          4
#define  VEX_DATA_BUFFER_SIZE  16

struct {
      unsigned char  header_aa;
      unsigned char  header_55;
      unsigned char  message_type;
      unsigned char  datalen;
      unsigned char  analog_0h;
      unsigned char  analog_0l;
      unsigned char  analog_1h;
      unsigned char  analog_1l;
      unsigned char  digital_0;
      unsigned char  digital_1;
      unsigned char  checksum;
     } datas;

union  { 
   datas   data;     
   unsigned char    buffer[VEX_DATA_BUFFER_SIZE];  
  } vexdata;

vexdata MyVexDataRx;

task main()
{
  int    i;
  string str;
  
  MyVexDataRx.data.header_aa    = 0xAA;
  MyVexDataRx.data.header_55    = 0x55;
  MyVexDataRx.data.message_type = 0x01;
  MyVexDataRx.data.datalen      = 0x06;
  
  MyVexDataRx.data.digital_0    = 0x11;
  MyVexDataRx.data.digital_1    = 0x22;
  
  for(i=0;i<VEX_DATA_BUFFER_SIZE;i++) {  
    sprintf(str, "%02X ", MyVexDataRx.buffer* );
    writeDebugStream(str);
  }
  writeDebugStreamLine("");

  while(1)
     wait1Msec(100);
}

Using typedef does not work.

Nesting the structure within the union does not work (although I need to check that)

There are many other things in the example code I posted that will not port to ROBOTC, the pointer use for example. The OP is using EasyC so I was not thinking ROBOTC when I wrote that code. Actually, as I said before, I was not planing to post all of it.*

Thank you for this. I already have code that sends and receives strings on the Cortex, I thought the union methodology would be a great addition. I also will enhance my code based upon your state machine example that should help make it more robust.

I really appreciate your help getting these features working in Robot C.

Cheers Kb

Alright, I am kind of piecing together code right now.


typedef union _vexdata { 
    struct {
      unsigned char  header_aa;
      unsigned char  header_55;
      unsigned char  reply_type;
      unsigned char  datalen;
      unsigned char  js_1;
      unsigned char  js_2;
      unsigned char  js_3;
      unsigned char  js_4;
      unsigned char  button_56;
      unsigned char  button_78;
      unsigned char  accel_y;
      unsigned char  accel_x;
      unsigned char  accel_z;
      unsigned char  checksum;
     } data;
     
   unsigned char    buffer[16];  
  } vexdata;

vexdata  MyVexData;

So what does all this mean? I have never seen these functions used before, so I am curious to what they do.

Edit: I am looking at your PS3 code thing and editing it from there.

There’s an expression that applies here about grandmothers and eggs, look it up. It was, and still is, arduino code from a working project.

You could, but if the string contains NULL characters then it’s a problem. Serial is a sub class of stream, stream has the function for writing a string, it looks like this.

size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); }
    

It’s calling a function to write a buffer and uses strlen to calculate the length of the string. strlen will look for the terminating NULL character in the string. This is the function to write the buffer.

size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    n += write(*buffer++);
  }
  return n;
}

So what happens is that write is called for each character in the string, by using a simple loop and sending each character my loop achieves the same thing without the issue of needing to make sure there are no NULL chars in it and with less processor overhead.

Did you read this post?

https://vexforum.com/showpost.php?p=313120&postcount=96

That’s the best I can do, perhaps it’s time to start reading Kernighan & Ritchie.

You should go back and re-read this post Link,

I needed to review it a couple times and reading this statement:

“So first of all we need somewhere to store the received information. Although a simple array will work, this example uses a union of both an array and a structure. A structure is a way of grouping several variables into one object. A union is a way of accessing the same region of memory in two (or more) different ways. This is very convenient when, for example, you want to be receiving a stream of bytes and placing them in a continuous array but afterwards access them by name. Here is a simple union that I will use for receiving data.”

you can see he has provided two ways to access the same piece of memory, The serial code handler can work at the byte level with the array definition, while elsewhere in your code you can reference the variables directly by name which makes code more readable.

I don’t have a good example yet as I am working this while doing a couple other things. If I get something working in Robot C I will post later.

I hope this helps explain what’s going on.

And yes you can write a string as well as is the case with many challenges there are many ways to solve them. My first “successful” attempt at getting the Cortex and Arduino to interface serially used the string write method.

Cheers Kb

Ah good point on the null characters, I haven’t done this type of code in so long I forgotten so much.

Cheers Kb

I’m sorry, this isn’t my day job :frowning:

I’ll go and read the post from before.

Edit: I don’t have a day job…

Ok, I have kind of changed the PS3 code a bit, but I have not paid any attention to anything after the void loop(){. Here is what I have so far:

// Storage for the vex communications data
typedef union _vexdata { 
    struct {
      unsigned char  header_aa;
      unsigned char  header_55;
      unsigned char  reply_type;
      unsigned char  datalen;
      unsigned char  js_1; // Pot 1
      unsigned char  js_2; // Pot 2
      unsigned char  button_1; // Button 1
      unsigned char  checksum;
     } data;
     
   unsigned char    buffer[10];  
  } vexdata;

vexdata  MyVexData;

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Arduino setup function                                                     */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void setup()
{
    
// reset USB shield - this is needed for the sparkfun version
// Initialize the VEX data structures
    VexDataInit();
    pinMode(2,INPUT);
 
// Open the serial port, luckily it defaults to 8bit, 1 stop, no parity
// as this is not easily changed
    Serial.begin(115200);    
    
// Initialize the USB class
// Do not use any debug print statements as we are using the serial
// port for the VEX communications

}

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Arduino main loop                                                          */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void loop(){
  static  uint32_t  timer = 0;
    
    // Run every 50mS
  if(millis() - timer < 50){
    return;     
  }
  timer =  millis();

  MyVexData.data.js_1 = analogRead(A0);
  MyVexData.data.js_2 = analogRead(A1);
  MyVexData.data.button_1 = digitalRead(2);
    
  VexDataChecksum();

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Initialize the VEX data                                                    */
/*                                                                             */
/*-----------------------------------------------------------------------------*/


}

void VexDataInit(){
    int  i;
    
    // clear all
    for(i=0;i<16;i++){
      MyVexData.buffer* = 0;
    }
    
    // Initialize packet  
    MyVexData.data.header_aa  = 0xAA;
    MyVexData.data.header_55  = 0x55;
    MyVexData.data.reply_type = 0x39;
    MyVexData.data.datalen    = 0x0A;
}

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Calculate the checksum for the VEX data                                    */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void VexDataChecksum(){
  int  i;
  int  cs = 0;
   
  for(i=4;i<13;i++){
    cs += MyVexData.buffer*;
    MyVexData.data.checksum = 0x100 - (cs & 0xFF);
  }
}

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Debugging print for the VEX data                                           */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

//#ifdef  DEBUG

void VexDataPrint(){
  int  i;
  char  str[4];
    
  for(i=0;i<14;i++){
    sprintf(str, "%02X ", MyVexData.buffer*); 
    Serial.print(str);
  }
      
  Serial.println("");    
}

//#endif

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  send the VEX data to the serial port                                       */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void VexDataTransmit(){
  int  i;
    
  for(i=0;i<14;i++){
    Serial.write( MyVexData.buffer* );
  }
}

I understand, I was worried that instead of helping that post would confuse you even more and that seems to be the case. The problem is that you are on a steep learning curve trying to accomplish the serial communications task whilst trying to learn the C programming language. Unfortunately your early attempts to transmit and receive data had common problems that we tend solve in certain ways that are sometimes a little advanced for a beginner. For example, the improvement you saw when increasing baud rate from 9600 to 115200 was probably due to much less time spent in the transmit loop and therefore much less time with interrupts turned off. The speed increase helped but the reason was really just a byproduct of another underlying problem you did not know about. Anyway, I will try and come up with a simpler example for you.

Here is another example, it is again designed to be used between two arduino boards, one transmitting the other receiving. This code does away with all the “clever” stuff, no structures, no inter byte timeouts, simple ASCII protocol. The message sends the same data as before, two 16 bit analog values, two 8 bit digital values, the ASCII message looks like this.

M:0000,1111,22,33:

The message starts with two characters, “M:”

next analog value 0 is sent as a 4 digit hex number, next analog 1 as a 4 digit hex number, digital 0 is next then digital 1. The message terminates with another colon. Lets say analog 0 is 100, analog 2 is 200, everything else is 0 the message would be.

M:0064,00C8,00,00:

lets break down the code.

#include <SoftwareSerial.h>

// Uncomment to compile for the transmitter
//#define  TRANSMITTER  1

#define  DEBUG  1

#define  BUFFER_SIZE    32

// Storage for the vex communications data
char    TxBuffer BUFFER_SIZE ];  
char    RxBuffer BUFFER_SIZE ];  

// use softwareSerial for receive
SoftwareSerial mySerial(6, 7); // RX, TX

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Arduino setup function                                                     */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void setup()
{
    int  i;
    // initialize the digital pin 13 as an output.
    pinMode(13, OUTPUT);  
    
    // Clear buffers
    for(i=0;i<BUFFER_SIZE;i++)
      TxBuffer i ] = 0;
    for(i=0;i<BUFFER_SIZE;i++)
      RxBuffer i ] = 0;
      
    // Open the software serial port, luckily it defaults to 8bit, 1 stop, no parity
    // as this is not easily changed
    // This is used for receive data so we can use the concole for debug
    mySerial.begin( 38400 );
       
    // console and transmit data
    Serial.begin( 38400 );  

    // Software alive
    Serial.print(F("\r\nSerial demo code"));
}

The setup function, the transmit and receive buffers are now simple arrays. I’m using the console serial port for transmit and the software serial for receive.

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Arduino main loop                                                          */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void loop()
{
    static  uint32_t  timer = 0;
            int       analogValue0;
            int       analogValue1;
    unsigned char     digitalValue0;
    unsigned char     digitalValue1;
            
#ifndef  TRANSMITTER
    // Run the receive task often
    serialRxTask();
#endif    

    // Run every 50mS
    if(millis() - timer < 50)
      return;     
    timer =  millis();
    
#ifdef TRANSMITTER
    // Read Analog channel 0
    analogValue0 = analogRead(A0);    
    // Read Analog channel 1
    analogValue1 = analogRead(A1);    

    // Clear digital data
    digitalValue0 = 0;
    digitalValue1 = 0;

    // Read some digital pins
    if( digitalRead(8) )
      digitalValue0 |= 0x01;
    if( digitalRead(9) )
      digitalValue0 |= 0x02;
    if( digitalRead(10) )
      digitalValue0 |= 0x04;
    if( digitalRead(11) )
      digitalValue0 |= 0x08;
    
    // Create message
    sprintf( TxBuffer,"M:%04X,%04X,%02X,%02X:\n", analogValue0, analogValue1, digitalValue0, digitalValue1 );

    // Transmit data
    Serial.write( TxBuffer );
#endif
}

The main loop. For the receiver keep calling the serialRxTask() function. For the transmitter use sprintf to create the transmit data. Send new data every 50mS (20 messages a second).

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Message receive task                                                       */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void
serialRxTask()
{
    static  int  serialRxState      = 0;
    static  int  serialDataReceived = 0;

    int    c;
    
    // check for a received character
    if( ! mySerial.available() )
      return;

    // Read char
    c = mySerial.read();

    // A new character has been received so process it.
    
    // Where are we in the message parsing process ?
    switch( serialRxState )
      {
      case  0:
          // look for first header byte
          if( c == 'M' )
            {
            serialDataReceived = 0;
            RxBuffer serialDataReceived++ ] = c;
            serialRxState++;
            }
          break;

      case  1:
          // look for second header byte
          if( c == ':' )
            {
            // Header found
            RxBuffer serialDataReceived++ ] = c;
            serialRxState++;
            }
          else
            {
            // Bad message
            serialRxState = 0;
            }
          break;

      case  2:
          // store the data
          RxBuffer serialDataReceived ] = c;
          
          // check for buffer overflow
          if( ++serialDataReceived >= BUFFER_SIZE )
            {
            // Error
            serialRxState = 0;
            }            

          // look for end of message
          if( c == ':' )
            {
            // Terminate rx buffer with a NULL
            RxBuffer serialDataReceived ] = 0;
            
            // Decode message          
            serialRxDecode();
            
            // done
            serialRxState = 0;
            }

          break;
          
      default:
          serialRxState = 0;
          break;
      }  
  
}

The receive task. A bit long but not really complex. Look for another receive character, check for the header in two parts, first the “M” and then the “:”, keep receiving data until either the buffer overflows (an error) or the terminating character is found “:”. Decode the receive buffer.

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Message decoding                                                           */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void
serialRxDecode()
{
   char  str[32];
   long  analog0;
   long  analog1;
   char  *p;
 
   // Show buffer for DEBUG  
   Serial.write( RxBuffer );
   Serial.write("\n");
   
   // an example, get the first two analog values
   
   p = &(RxBuffer[2]);
   analog0 = strtol( p, &p, 16 );
   p++; // skip comma
   analog1 = strtol( p, &p, 16 );

   // DEBUG - show the value of A0 and A1
   sprintf(str,"A0  %ld, A1 %ld\n", analog0, analog1 );
   Serial.write( str );
}
 

Decode the message, here all I’m doing as it’s a demo is printing the message to the console and using strtol to get the first two analog values. Code is attached.

Enjoy.
serialDemo2.zip (2.52 KB)

Ok, that seems easier. Would it be hard to port the receiving end over to EasyC Pro code? I wouldn’t think so because most of the syntax is the same. Instead of Serial.write, it would be print screen. I think I can port it over, I think.

Should be pretty easy, the strtol function may not exist in the receive message decoder (or in fact any long support) but you can decode the message in a number of other ways.

Here’s a port of the receiver to EasyC Pro. I have not tried this as I do not have a PIC but it compiles and should work. It is basically the same code but with a couple of small functions added, longs were supported but strtol is not.

// receiver.c : implementation file
#include "API.h"

#define  BUFFER_SIZE    32

// Storage for the vex communications data
char    RxBuffer BUFFER_SIZE ];  

void    serialRxInit(void);
void    serialRxTask(void);
void    serialRxDecode(void);

void
serialRxInit()
{
    int     i;

    // Clear buffer
    for(i=0;i<BUFFER_SIZE;i++)
      RxBuffer i ] = 0;

    // Init serial port 2
    OpenSerialPortTwo( BAUD_38400 );
}


/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Message receive task                                                       */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void
serialRxTask()
{
    static  int  serialRxState      = 0;
    static  int  serialDataReceived = 0;

    int    c;
    
    // check for a received character
    if( GetSerialPort2ByteCount() == 0 )
      return;

    // Read char
    c = ReadSerialPortTwo();

    // A new character has been received so process it.
    
    // Where are we in the message parsing process ?
    switch( serialRxState )
      {
      case  0:
          // look for first header byte
          if( c == 'M' )
            {
            serialDataReceived = 0;
            RxBuffer serialDataReceived++ ] = c;
            serialRxState++;
            }
          break;

      case  1:
          // look for second header byte
          if( c == ':' )
            {
            // Header found
            RxBuffer serialDataReceived++ ] = c;
            serialRxState++;
            }
          else
            {
            // Bad message
            serialRxState = 0;
            }
          break;

      case  2:
          // store the data
          RxBuffer serialDataReceived ] = c;
          
          // check for buffer overflow
          if( ++serialDataReceived >= BUFFER_SIZE )
            {
            // Error
            serialRxState = 0;
            }            

          // look for end of message
          if( c == ':' )
            {
            // Terminate rx buffer with a NULL
            RxBuffer serialDataReceived ] = 0;
            
            // Decode message          
            serialRxDecode();
            
            // done
            serialRxState = 0;
            }

          break;
          
      default:
          serialRxState = 0;
          break;
      }  
}

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Message decoding                                                           */
/*                                                                             */
/*-----------------------------------------------------------------------------*/


// check for valid Hexadecimal character
int
ishexchar( char c )
{
    if( (c >= '0') && (c <= '9') )
        return(1);
    else        
    if( (c >= 'A') && (c <= 'F') )
        return(1);
    else
        return(0);
}

// convert hex string to integer
unsigned int
hextoi( char *p )
{
    unsigned int val = 0;
    int c;
    
    // NULL ptr then return
    if(p == NULL)
        return(0);
    
    // cycle through chars
    while( *p != 0 && ishexchar(*p) )
        {
        // convert ASCII to number
        c = (*p - '0');
        if(c > 9)
            c -= 7;
        
        // accumulate
        val = (val * 16) + c;

        // next char
        p++;
        }
        
    return(val);
}

void
serialRxDecode()
{
   unsigned int   analog0;
   unsigned int   analog1;
   char  *p;
 
   // Show buffer for DEBUG  
   PrintToScreen ( "%s\n" , RxBuffer) ; 

   // an example, get the first two analog values
   
   p = &(RxBuffer[2]);
   analog0 = hextoi( p );
   p = &(RxBuffer[7]);
   analog1 = hextoi( p );

   // DEBUG - show the value of A0 and A1
   PrintToScreen ( "A0  %ld, A1 %ld\n", analog0, analog1 );
}

Add the code above to a new user source file, call it receiver.c or similar.

Add the following to the EasyC Initialize function.

serialRxInit();

Add the following to the while loop in operator control.

serialRxTask();

Add the following to UserInclude.h

void    serialRxInit(void);
void    serialRxTask(void);
void    serialRxDecode(void);

Let us know how it goes :slight_smile:

.

Ok. Sorry I haven’t been online in FOREVER. Good thing is, school starts in a week, so I still have a little bit of extra time. So I add serialRxInit(); in a user code block to the first part of the code, right? Then I add serialRxTask(); to where all the driving stuff is, right? Then you said to add void serialRxInit(void);
void serialRxTask(void);
void serialRxDecode(void); to the userinclude. Alright, I think I can handle this :smiley:

I’ll post how it goes soon.

So in the block diagram, will I be able to set servos to the analog values after I call the SerialRxTask(); command? Are they globals so I can access them from any where in the program?

Alright. I tried the code, and it doesn’t seem to look like any thing is going on. I checked both serial ports and they wern’t printing anything. I modified the code a little bit, still nothing. I tried taking the transmit cord from the arduino and putting it into the recieve on the arduino, nothing.