Shop OBEX P1 Docs P2 Docs Learn Events
duino to SPIN. Memcpy, Pointer, Struct Containing Many Types Including Float — Parallax Forums

duino to SPIN. Memcpy, Pointer, Struct Containing Many Types Including Float

I need to take a struct containing floats and other types, measure the size of the incoming (serial) struct, do a bitwise and with a pointer, and then memcpy. In addition in the duino code they can grab for example VData.AP. I need to be able to get data from serial, store it, and call it. This really has me stuck. The particular things I think I'm having trouble with are commented in the code below as a problem.

I really prefer to do this project with a Prop instead of a duino.

How do I store the incoming serial data in a place I can call it, and how do I call it?

Thanks for any help.
uint16_t * address; //-----------------------------PROBLEM
byte buffer[256]; //address for temporary storage and parsing buffer
uint8_t structSize;

struct VesselData //------------------------------PROBLEM
{
    byte id;                //1
    float AP;               //2
    float PE;               //3
    float SemiMajorAxis;    //4
    float SemiMinorAxis;    //5
    float VVI;              //6
    float e;                //7
    float inc;              //8
    float G;                //9
    long TAp;               //10
    long TPe;               //11
    float TrueAnomaly;      //12
    float Density;          //13
    long period;            //14
    float RAlt;             //15
    float Alt;              //16
    float Vsurf;            //17
    float Lat;              //18
    float Lon;              //19
    float LiquidFuelTot;    //20
    float LiquidFuel;       //21
    float OxidizerTot;      //22
    float Oxidizer;         //23
    float EChargeTot;       //24
    float ECharge;          //25
    float MonoPropTot;      //26
    float MonoProp;         //27
    float IntakeAirTot;     //28
    float IntakeAir;        //29
    float SolidFuelTot;     //30
    float SolidFuel;        //31
    float XenonGasTot;      //32
    float XenonGas;         //33
    float LiquidFuelTotS;   //34
    float LiquidFuelS;      //35
    float OxidizerTotS;     //36
    float OxidizerS;        //37
    uint32_t MissionTime;   //38
    float deltaTime;        //39
    float VOrbit;           //40
    uint32_t MNTime;        //41
    float MNDeltaV;         //42
    float Pitch;            //43
    float Roll;             //44
    float Heading;          //45
    uint16_t ActionGroups;  //46 status bit order:SAS, RCS, Light, Gear, Brakes, Abort, Custom01 - 10
    byte SOINumber;         //47 SOI Number (decimal format: sun-planet-moon e.g. 130 = kerbin, 131 = mun)
    byte MaxOverHeat;       //48  Max part overheat (% percent)
    float MachNumber;       //49
    float IAS;              //50  Indicated Air Speed
};

VesselData VData;

boolean KSPBoardReceiveData() {
  if ((rx_len == 0)&&(Serial.available()>3)){
    while(Serial.read()!= 0xBE) {
      if (Serial.available() == 0)
        return false;  
    }
    if (Serial.read() == 0xEF){
      rx_len = Serial.read();   
      id = Serial.read(); 
      rx_array_inx = 1;

      switch(id) {
      case 0:
        structSize = sizeof(HPacket);   
        address = (uint16_t*)&HPacket;     
        break;
      case 1:
        structSize = sizeof(VData);  //----------------------------PROBLEM 
        address = (uint16_t*)&VData;  //--------------------------PROBLEM   
        break;
      }
    }

    //make sure the binary structs on both Arduinos are the same size.
    if(rx_len != structSize){
      rx_len = 0;
      return false;
    }   
  }

  if(rx_len != 0){
    while(Serial.available() && rx_array_inx <= rx_len){
      buffer[rx_array_inx++] = Serial.read();
    }
    buffer[0] = id;

    if(rx_len == (rx_array_inx-1)){
      //seem to have got whole message
      //last uint8_t is CS
      calc_CS = rx_len;
      for (int i = 0; i<rx_len; i++){
        calc_CS^=buffer[i];
      } 

      if(calc_CS == buffer[rx_array_inx-1]){//CS good
        memcpy(address,buffer,structSize); //---------------------PROBLEM
        rx_len = 0;
        rx_array_inx = 1;
        return true;
      }
      else{
        //failed checksum, need to clear this out anyway
        rx_len = 0;
        rx_array_inx = 1;
        return false; 
      }
    }
  }

  return false;
}



void loop()
{
  KSPBoardReceiveData() //The loop is abbreviated. This is the function I'm stuck on.
}

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2015-11-03 03:27
    The Prop uses aligned data types so bytes or uint8s can be stored at any address, ints or uint16s can only be stored at word addresses (even). floats, longs, and uint32s can only be stored at long addresses (multiple of 4). Spin rearranges variables so they're aligned properly or (in assembly) pads them with dummy variables so the alignment comes out correctly. I'm not sure whether the C/C++ compiler pads them or flags them with warnings or both. The way to handle this sort of alignment issue when you're getting external data is to read the data into a buffer, then copy the data using memcpy (variable) by variable into the elements of a structure that is properly aligned. You can either use a lot of memcpy statements or create a table containing the size of each variable in bytes and its address or offset in the structure. A simple loop can move through the buffer doing a memcpy for each variable.

    In your particular case, only the first variable (id) is not aligned properly. You could declare it as a "byte id[4];" and do a memcpy of the serial data starting with the last byte of the variable ("&id[3]"). If your serial data is not "little endian" with the least significant byte first in the data stream, you'll also have to rearrange the bytes of each value.
  • Thanks for the help Mike.

    Your description of how to align the data is helpful.

    I've got a question. I don't know of a way to do a memcpy in SPIN. What am I missing here?

    Thanks again.
  • Look at the BYTEMOVE statement in the Propeller Manual. It's similar to memcpy.
  • OK I'm going to the end of the pool where my feet don't touch yet.

    Is my pseudo code in the ball park?

    repeat i from 0 to totalbytes
    buffer = serial-in

    if checksum = sentchecksum
    bytemove( storage, buffer, totalbytes)

    id = buffer 0

    somehow make AP = buffer 1 through 5

    loop through and put together variables from their region of the buffer




    Is there a routine for taking 4 bytes and turning them into a float?

    Thanks Mike. I would have been fumbling around forever trying to find BYTEMOVE.
  • So at this point I think I have the data brought in.

    When trying to grab the easiest of them all, the first byte which is actually a byte I personally know the value of, it doesn't work.

    How would I grab a float from this, assuming I know for example that it exists starting at buffer[1]?
    repeat rx_array_inx from 1 to rx_len         
          buffer[rx_array_inx] := KS.rx       
        buffer[0] := id
    
    bytemove(@buffer, @address, rx_len)
    
    //I know id, and therefore buffer[0] or @buffer[0], should be either 0 or 1
    
    //@buffer[0] == 143
    //@address[0] == 143
    
    
  • The value of AP comes from buffer[ 1 ] through buffer[ 4 ].

    The floating point routines for the Propeller use the standard IEEE floating point format. If your serial data is also in IEEE format, you don't have to do any conversion (other than byte order if it's not least significant byte first).

    The most important thing for you is to document the format of the serial data which you haven't done here (including the formats of each data item ... "little endian" or "big endian")
  • As Mike said, you need to verify whether the data in the struct is little-endian or big-endian. If it's little-endian you won't have to do a byte swap. You also need to determine if the struct is packed or not. If it is packed the 16-bit and 32-bit variables in the struct will not be aligned because of the 1-byte ID at the beginning. If it is not packed there will be padding bytes that are used to align 16-bt and 32-bit variables. C normally stores structs as unpacked.

    Why don't you write your program in C? Then you won't have to convert everything to Spin. Do you have a link for the spec on the transmission protocol? It appear to consist of a 16-bit sync word, $BEEF, followed by an 8-bit length value, and a checksum at the end. What is the length value that you are receiving? It should match the size of the struct plus one checksum byte.
  • The Arduino is apparently little endian. I'm trying to port Arduino code so I'd think it's that. I really don't know how to find out what endian type is used by the mod.

    I do have ID working now. So I need to try to put together the float, and if it doesn't come out right I know there might be an endian issue.

    I don't see anywhere in the Arduino code that seems to be converting endian types, so my guess and hope is that it's being sent with little endian, and that's what the Prop is expecting.

    My main reason for doing this in SPIN is because I suspect it will be useful to read all my buttons in PASM, and the vast majority of drivers in OBEX are in SPIN. So for example TV is in SPIN, not C. My Joystick Driver is in SPIN and PASM.

    I don't know exactly how many features I'm going to want in this but it seems to have the most possibility I'm going to want SPIN. Could be wrong, it's just my current thinking.

    Thanks for the help so far guys.
  • I can't find the routines to take buffer[1..4] and turn it in to a float. Do you have a link or terminology to search for?

    Thanks!
  • ElectrodudeElectrodude Posts: 1,658
    edited 2015-11-03 16:09
    I can't find the routines to take buffer[1..4] and turn it in to a float. Do you have a link or terminology to search for?

    Thanks!
    bytemove(@long_var_that_will_hold_our_float, @buffer[1], 4)
    

    Spin doesn't have types. Just store floats in longs, and make sure you use FP ops on them (from F32.spin or something).
  • Thanks Electrodude.

    It's getting me the right value for id, so I must be doing something wrong somewhere else. The value it gives for AP is clearly wrong, at least after I send it via serial as a dec (it's a float).

    Hopefully I can figure out where my problem is, because if so that's likely the last piece of the puzzle before I can start checking altitudes etc.
  • Thanks Electrodude.

    It's getting me the right value for id, so I must be doing something wrong somewhere else. The value it gives for AP is clearly wrong, at least after I send it via serial as a dec (it's a float).

    Hopefully I can figure out where my problem is, because if so that's likely the last piece of the puzzle before I can start checking altitudes etc.

    You can't send a float as a dec like that. As Spin doesn't have proper types and assumes everything is a signed integer, the dec function thinks your float is just an int, and prints out an invalid number. Try FloatString.spin for printing floats as strings.
  • Keith YoungKeith Young Posts: 569
    edited 2015-11-03 17:02
    Thanks Electrodude.

    I'm doing something wrong somewhere.

    No matter what I always get 121765XXXX with XXX often being 4000.

    I'm correctly polling address[0] for the id.

    Another interesting thing is I can grab address[1] and address[3] (checking for padding) and they get the same result.
      longmove(@templong, @address[1], 1)
      
      'templong := address[1..4]
      'templong := FM.FFloat(templong)
      
      XB.str(String("addr[1..4]= "))
      XB.dec(templong)
      XB.str(FS.FloatToString(FM.FFloat(templong)))
      XB.CR
    
  • The LONGMOVE won't work the way you've written it. Use BYTEMOVE and give a length in terms of bytes. The LONGMOVE requires addresses that are multiples of 4 and the length is the number of 4 byte long words. Similarly, WORDMOVE requires addresses that are multiples of 2 and the length is the number of 2 byte words.

    LONGMOVE (and other statements that expect to get the address of a 32-bit long word) will ignore the least significant two bits of the address provided.
  • I think your guys help just led me to the solution!!!

    I don't yet know if I can do comparisons or use these values to display on 7 segment etc, but I got it to go through to PST correctly.

    One interesting thing is I could pull a longmove from @buffer[1] through @buffer[4] with a size 1 and still get the same value.

    bytemove with length of 4 also worked great.

    Thanks a ton for the help guys. I couldn't have gotten this far without you.

    Now to see if I can turn on an LED when my altitude gets too low. Awesome!
  • Yep!!! With FRound I can say:

    if apogee > desired and perigee > desired
    turn on LED
    say "achieved orbit"

    Thanks guys. This is amazing.
  • Assuming that the buffer variable is long-aligned, LONGMOVE(@AP,@buffer[ 1 ], 1) will transfer buffer[ 0 ] through buffer[ 3 ] into AP. The Propeller hardware ignores the two least significant bits of the address when it fetches a 32-bit long word. As a result, this fetch starts at @buffer[ 0 ] and goes through @buffer[ 3 ] all done in a single operation. The same sort of thing occurs when this 32-bit value is stored at @AP.
  • Cool. Thanks Mike.
Sign In or Register to comment.