NT Bus Protocol: Difference between revisions

From STorM32-BGC Wiki V1
Jump to navigation Jump to search
 
(8 intermediate revisions by the same user not shown)
Line 27: Line 27:
  #define NTBUS_ID_MOTORROLL                5
  #define NTBUS_ID_MOTORROLL                5
  #define NTBUS_ID_MOTORYAW                6
  #define NTBUS_ID_MOTORYAW                6
#define NTBUS_ID_CAMERA                  7
#define NTBUS_ID_JOYSTICK                8
#define NTBUS_ID_KEYS                    9
#define NTBUS_ID_PWMOUT                  10
  #define NTBUS_ID_LOGGER                  11
  #define NTBUS_ID_LOGGER                  11
#define NTBUS_ID_IMU3                    12


== Communication Details ==
== Communication Details ==
Line 38: Line 33:
=== Communication from Master to Slaves ===
=== Communication from Master to Slaves ===


Every message emitted by the master is started with a start byte, which has three fields:
Every message emitted by the master begins with a start byte, which has three fields:


  bit 7:        always 1, indicates that it is a start byte
  bit 7:        always 1, indicates that it is a start byte
Line 59: Line 54:
The start byte may be followed by further bytes, but these bytes must have their 7th bit set to low!  
The start byte may be followed by further bytes, but these bytes must have their 7th bit set to low!  


Transmitted data must be protected by a crc byte at its end. It is calculated by a xor over all data bytes, with the 7th bit set to low.
Transmitted data must be protected by a crc byte. It is calculated by a XOR over all data bytes, with the 7th bit set to low.


'''Examples:'''
'''Examples:'''
Line 76: Line 71:
=== Communication from Slave to Master ===
=== Communication from Slave to Master ===


Whenever a NT module sends data to the STorM32, it first must enable it's Tx line, wait 2 us, send the data, wait 2us, and then disable the Tx line.
Whenever a NT module sends data to the STorM32, it first must enable it's Tx line, send the data, and then disable the Tx line.


In contrast to the messages from master to slaves, full bytes can be send (i.e. the 7th bit doesn't have to be 0), since by design a slave never sends a command.
In contrast to the messages from the master to slaves, full bytes can be send, i.e. the 7th bit doesn't have to be 0, since by design a slave never sends a command.  


== Error Handling ==  
== Error Handling ==  
Line 85: Line 80:


In addition the receiving UARTs should check for error conditions offered by the hardware. For a STM32F103 this would be e.g. the overrun, noise, and frame flags.
In addition the receiving UARTs should check for error conditions offered by the hardware. For a STM32F103 this would be e.g. the overrun, noise, and frame flags.
== Implementation Details ==
<nowiki>
//STorM32 firmware v0.93
//9. Feb. 2015
// NT MODULE ID LIST
#define NTBUS_ID_ALLMODULES              0
#define NTBUS_ID_IMU1                    1
#define NTBUS_ID_IMU2                    2
#define NTBUS_ID_MOTORALL                3
#define NTBUS_ID_MOTORPITCH              4
#define NTBUS_ID_MOTORROLL                5
#define NTBUS_ID_MOTORYAW                6
#define NTBUS_ID_LOGGER                  11
// STARTBYTE flags and masks
#define NTBUS_STX                        0x80 // 0b 1000 0000
#define NTBUS_SFMASK                      0x70 // 0b 0111 0000
#define NTBUS_IDMASK                      0x0F // 0b 0000 1111
#define NTBUS_FLASH                      0x70 // 0b 0111 0000
#define NTBUS_RESET                      0x50 // 0b 0101 0000
#define NTBUS_SET                        0x40 // 0b 0100 0000
#define NTBUS_GET                        0x30 // 0b 0011 0000
#define NTBUS_TRIGGER                    0x10 // 0b 0001 0000
#define NTBUS_CMD                        0x00 // 0b 0000 0000
// COMMANDS , this is the command byte after a NTBUS_STX|NTBUS_CMD|NTBUS_ID startbyte
typedef enum {
  NTBUS_CMD_GETSTATUS = 1,
  NTBUS_CMD_GETVERSIONSTR,
  NTBUS_CMD_GETBOARDSTR,
  NTBUS_CMD_GETCONFIGURATION,
  NTBUS_CMD_ACCGYRO1RAWDATA_V1 = 32, //DEPRECTAED
  NTBUS_CMD_ACCGYRO2RAWDATA_V1,      //DEPRECTAED
  NTBUS_CMD_ACCGYRODATA_V1,          //DEPRECTAED
  NTBUS_CMD_PIDDATA, //35
  NTBUS_CMD_PARAMETERDATA, //36
  NTBUS_CMD_AHRS1DATA, //37
  NTBUS_CMD_AHRS2DATA,
  NTBUS_CMD_ACCGYRO3RAWDATA_V1, //39 //DEPRECATED
  NTBUS_CMD_ACCGYRO1RAWDATA_V2, //40
  NTBUS_CMD_ACCGYRO2RAWDATA_V2,
  NTBUS_CMD_ACCGYRO3RAWDATA_V2,
  NTBUS_CMD_ACCGYRO1DATA_V2, //43
  NTBUS_CMD_ACCGYRO2DATA_V2,
  NTBUS_CMD_READSETUP = 120,
  NTBUS_CMD_WRITESETUP,
  NTBUS_CMD_STORESETUP,
  NTBUS_CMD_NOTUSED = 0xFF
} NTBUSCMDTYPE;
// NTBUS_CMD_GETSTATUS
// from STorM32: stx, cmdbyte -> to STorM32: 2 x u8 cmddata, crc
PACKED(
typedef struct {
  char Status;
  uint8_t State;
}) tNTBusCmdGetStatusData;
#define NTBUS_CMDGETSTATUS_DATALEN        (sizeof(tNTBusCmdGetStatusData))
// NTBUS_CMD_GETVERSIONSTR
// from STorM32: stx, cmdbyte -> to STorM32: 16 x u8 cmddata, crc
PACKED(
typedef struct {
  char VersionStr[16];
}) tNTBusCmdGetVersionStrData;
#define NTBUS_CMDGETVERSIONSTR_DATALEN    (sizeof(tNTBusCmdGetVersionStrData))
// NTBUS_CMD_GETBOARDSTR
// from STorM32: stx, cmdbyte -> to STorM32: 16 x u8 cmddata, crc
PACKED(
typedef struct{
  char BoardStr[16];
}) tNTBusCmdGetBoardStrData;
#define NTBUS_CMDGETBOARDSTR_DATALEN      (sizeof(tNTBusCmdGetBoardStrData))
// NTBUS_CMD_GETCONFIGURATION
// from STorM32: stx, cmdbyte -> in STorM32: 1 x u16 cmddata, crc
PACKED(
typedef struct {
  uint16_t Configuration;
}) tNTBusCmdGetConfigurationData;
#define NTBUS_CMDGETCONFIGURATION_DATALEN (sizeof(tNTBusCmdGetConfigurationData))
// IMU MODULE
// SET: -
// GET: 3xi16 acc + 3xi16 gyro + 1xi16 temp + 1xu8 imu status
// status byte , is used for both internal bookkeeping and CMD command
#define NTBUS_IMU_STATUS_IMU_PRESENT      0x80
// configuration word
#define NTBUS_IMU_CONFIG_MPU6050          0x0001
#define NTBUS_IMU_CONFIG_MPU6000          0x0002
#define NTBUS_IMU_CONFIG_MPU9250          0x0004
#define NTBUS_IMU_CONFIG_MODELUNKNOWN    0x0000
#define NTBUS_IMU_CONFIG_MODELMASK        0x0007
#define NTBUS_IMU_CONFIG_I2C              0x0100
#define NTBUS_IMU_CONFIG_SPI            0x0200
#define NTBUS_IMU_CONFIG_BUSUNKNOWN      0x0000
#define NTBUS_IMU_CONFIG_BUSMASK          0x0300
// GET IMU imu status byte
#define NTBUS_IMU_IMUSTATUS_GYRODATA_OK  0x01
#define NTBUS_IMU_IMUSTATUS_ACCDATA_OK    0x02
// NTBUS_GET
// to STorM32: 6xi16 + 1xi16 + 1xu8 + crc = 15+1 bytes
PACKED(
typedef struct {
  int16_t AccX;    //+-2g
  int16_t AccY;    //+-2g
  int16_t AccZ;    //+-2g
  int16_t GyroX;    //+-1000°/s
  int16_t GyroY;    //+-1000°/s
  int16_t GyroZ;    //+-1000°/s
  int16_t Temp;
  uint8_t ImuStatus;
}) tNTBusGetImuData;
#define NTBUS_GETIMU_DATALEN              (sizeof(tNTBusGetImuData))
// MOTOR MODULE:
// SET: 1xu8 flags + 1xu8 vmax pitch + 1xi16 angle pitch + 1xu8 vmax roll + 1xi16 angle roll + 1xu8 vmax yaw + 1xi16 angle yaw
// GET: -
// configuration word
#define NTBUS_MOTOR_CONFIG_DEFAULT        0x0000
// enable flag byte
#define NTBUS_MOTOR_ENABLEFLAG_BEEP      0x40
#define NTBUS_MOTOR_ENABLEFLAG_GLOBAL    0x10 //this MUST agree with Motor flag
#define NTBUS_MOTOR_ENABLEFLAG_PITCH      0x01 //this MUST agree with Motor flag
#define NTBUS_MOTOR_ENABLEFLAG_ROLL      0x02 //this MUST agree with Motor flag
#define NTBUS_MOTOR_ENABLEFLAG_YAW        0x04 //this MUST agree with Motor flag
// NTBUS_SET
// from STorM32: data + crc = 10+1 bytes
// it has it's "own" way to ensure that the 7th bits are zero
PACKED(
typedef struct {
  uint8_t Flags;
  uint8_t VmaxPitch;
  int16_t AnglePitch;
  uint8_t VmaxRoll;
  int16_t AngleRoll;
  uint8_t VmaxYaw;
  int16_t AngleYaw;
}) tNTBusSetMotorAllData;
#define NTBUS_SETMOTORALL_DATALEN        (sizeof(tNTBusSetMotorAllData))
// LOGGER MODULE:
// SET: 1xu32 + 7xu8 + 5xu16 + 6xi16
// GET: -
// configuration word
#define NTBUS_LOGGER_CONFIG_DEFAULT      0x0000
// SET LOGGER imu status byte
#define NTBUS_LOGGER_IMUSTATUS_GYRODATA_OK    NTBUS_IMU_IMUSTATUS_GYRODATA_OK //0x01
#define NTBUS_LOGGER_IMUSTATUS_ACCDATA_OK    NTBUS_IMU_IMUSTATUS_ACCDATA_OK //0x02
// NTBUS_SET
// from STorM32: 1xu32 + 7xu8 + 5xu16 + 6xi16 = 33 bytes
PACKED(
typedef struct {
  uint32_t TimeStamp32; // in us
  uint8_t Imu1received; // in 10us units
  uint8_t Imu1done;    // in 10us units
  uint8_t PIDdone;      // in 10us units
  uint8_t Motorsdone;  // in 10us units
  uint8_t Imu2received; // in 10us units
  uint8_t Imu2done;    // in 10us units
  uint8_t Loopdone;    // in 10us units
  uint16_t State;
  uint16_t Status;
  uint16_t Status2;
  uint16_t ErrorCnt;
  uint16_t Voltage;
  int16_t Imu1AnglePitch;
  int16_t Imu1AngleRoll;
  int16_t Imu1AngleYaw;
  int16_t Imu2AnglePitch;
  int16_t Imu2AngleRoll;
  int16_t Imu2AngleYaw;
}) tNTBusSetLoggerData;
#define NTBUS_SETLOGGER_DATALEN          (sizeof(tNTBusSetLoggerData))
#define NTBUS_SETLOGGER_HIGHBITSLEN      6 //33 bytes => 5 bytes required => 3xu16 = 6 bytes used
/* DEPRECATED VERSION1
// NTBUS_CMD_ACCGYRO1RAWDATA, NTBUS_CMD_ACCGYRO2RAWDATA
// from STorM32: 6xi16 = 12 bytes
PACKED(
typedef struct {
  int16_t axraw;
  int16_t ayraw;
  int16_t azraw;
  int16_t gxraw;
  int16_t gyraw;
  int16_t gzraw;
}) tNTBusCmdAccGyroRawDataV1;
#define NTBUS_CMDACCGYRORAWDATAV1_DATALEN      (sizeof(tNTBusCmdAccGyroRawDataV1))
#define NTBUS_CMDACCGYRORAWDATAV1_HIGHBITSLEN  2 //12 bytes => 2 bytes required => 1xu16 = 2 bytes used */
// NTBUS_CMD_ACCGYRO1RAWDATA, NTBUS_CMD_ACCGYRO2RAWDATA V2
// from STorM32: 7xi16 = 14 bytes
PACKED(
typedef struct {
  int16_t axraw;
  int16_t ayraw;
  int16_t azraw;
  int16_t gxraw;
  int16_t gyraw;
  int16_t gzraw;
  int16_t temp;
}) tNTBusCmdAccGyroRawDataV2;
#define NTBUS_CMDACCGYRORAWDATAV2_DATALEN    (sizeof(tNTBusCmdAccGyroRawDataV2))
#define NTBUS_CMDACCGYRORAWDATAV2_HIGHBITSLEN 2 //14 bytes => 2 bytes required => 1xu16 = 2 bytes used
/* DEPRECATED VERSION1
// NTBUS_CMD_ACCGYRODATA
// from STorM32: 7xi16 + 1xu8 + 7xi16 + 1xu8 = 30 bytes
PACKED(
typedef struct {
  int16_t ax1;
  int16_t ay1;
  int16_t az1;
  int16_t gx1;
  int16_t gy1;
  int16_t gz1;
  int16_t temp1;
  uint8_t ImuState1;
  int16_t ax2;
  int16_t ay2;
  int16_t az2;
  int16_t gx2;
  int16_t gy2;
  int16_t gz2;
  int16_t temp2;
  uint8_t ImuState2;
}) tNTBusCmdAccGyroDataV1;
#define NTBUS_CMDACCGYRODATAV1_DATALEN      (sizeof(tNTBusCmdAccGyroDataV1))
#define NTBUS_CMDACCGYRODATAV1_HIGHBITSLEN  6 //30 bytes => 5 bytes required => 3xu16 = 6 bytes used */
// NTBUS_CMD_ACCGYRODATA VERSION2
// from STorM32: 6xi16 + 1xu8 = 13 bytes
PACKED(
typedef struct {
  int16_t ax;
  int16_t ay;
  int16_t az;
  int16_t gx;
  int16_t gy;
  int16_t gz;
  uint8_t ImuState;
}) tNTBusCmdAccGyroDataV2;
#define NTBUS_CMDACCGYRODATAV2_DATALEN      (sizeof(tNTBusCmdAccGyroDataV2))
#define NTBUS_CMDACCGYRODATAV2_HIGHBITSLEN  2 //13 bytes => 2 bytes required => 1xu16 = 2 bytes used
// NTBUS_CMD_PIDDATA
// from STorM32: 6xi16 = 12 bytes
PACKED(
typedef struct {
  int16_t PIDCntrlPitch;
  int16_t PIDCntrlRoll;
  int16_t PIDCntrlYaw;
  int16_t PIDMotorCntrlPitch;
  int16_t PIDMotorCntrlRoll;
  int16_t PIDMotorCntrlYaw;
}) tNTBusCmdPidData;
#define NTBUS_CMDPIDDATA_DATALEN          (sizeof(tNTBusCmdPidData))
#define NTBUS_CMDPIDDATA_HIGHBITSLEN      2 //12 bytes => 2 bytes required => 1xu16 = 2 bytes used
// NTBUS_CMD_PARAMETERDATA
// from STorM32: 3xi16 + 16xu8 = 22 bytes
PACKED(
typedef struct{
  uint16_t Adr;
  uint16_t Value;
  uint16_t Format;
  char NameStr[16];
}) tNTBusCmdParameterData;
#define NTBUS_CMDPARAMETERDATA_DATALEN    (sizeof(tNTBusCmdParameterData))
#define NTBUS_CMDPARAMETERDATA_HIGHBITSLEN  4 //22 bytes => 4 bytes required => 2xu16 = 4 bytes used
// NTBUS_CMD_AHRSDATA
// from STorM32: 5xi16 = 10 bytes
PACKED(
typedef struct {
  int16_t rx;
  int16_t ry;
  int16_t rz;
  int16_t accconfidence;
  int16_t yawtarget;
}) tNTBusCmdAhrsData;
#define NTBUS_CMDAHRSDATA_DATALEN        (sizeof(tNTBusCmdAhrsData))
#define NTBUS_CMDAHRSDATA_HIGHBITSLEN    2 //10 bytes => 2 bytes required => 1xu16 = 2 bytes used
// IMU MODULE SPECIFIC routines
// try to detect module, 10 times max
// returns 0 or 0x01
// executed by STorM32
uint16_t ntbus_find_module(uint8_t id)
{
uint16_t i, ret;
  for( i=0; i<10; i++ ){
ntbus_putcmd( id, NTBUS_CMD_GETSTATUS );
    ret = ntbus_get( NTBUS_CMDGETSTATUS_DATALEN+1, 50 ); //wait only for 50 us
    if( ret ) return 1;
  }
  return 0;
}
// initialize imu module when it had been found
// returns 1 or 0x03
// executed by STorM32
uint16_t ntbus_ready_imumodule(uint8_t id)
{
uint16_t i, ret;
  for(i=0; i<100; i++){
    ntbus_putcmd( id, NTBUS_CMD_GETSTATUS );
    ret = ntbus_get( NTBUS_CMDGETSTATUS_DATALEN+1, 50 ); //wait only for 50 us
    if( ret &&( ntbus_buf[0] == NTBUS_IMU_STATUS_IMU_PRESENT )&&( ntbus_buf[1] == NTBUS_STATE_READY )){
      //read it once to startup
      ntbus_putsf_wflushall( id, NTBUS_TRIGGER );
      ntbus_putsf( id, NTBUS_GET );
      ntbus_get( NTBUS_GETIMU_DATALEN+1, 500 );
      return 3;
    }
    delay_ms(50);
  }
  return 1;
}
// send from imu module to STorM32
void ntbus_putimudata(void *imu, uint8_t imustatus)
{
uint8_t i; uint8_t crc; char c;
  crc = 0;
  for( i=0; i<NTBUS_GETIMU_DATALEN-1; i++ ){ c = *((u8*)(imu++)); ntbus_putc( c ); crc ^= c; }
  c = imustatus;
  ntbus_putc( c ); crc ^= c;
  ntbus_putc( crc );
}
// MOTOR MODULE SPECIFIC routines
// send from STorM32 to all motor modules
void ntbus_setmotoralldata(uint16_t enabledflag, uint16_t beep, tu16Angle vmax, tu16Angle angle)
{
uint8_t b, crc;
  b = 0;
  if( enabledflag & MOTORGLOBALENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_GLOBAL;
  if( enabledflag & MOTORPITCHENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_PITCH;
  if( enabledflag & MOTORROLLENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_ROLL;
  if( enabledflag & MOTORYAWENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_YAW;
  if( beep ) b |= NTBUS_MOTOR_ENABLEFLAG_BEEP;
  ntbus_putsf( NTBUS_ID_MOTORALL, NTBUS_SET ); //DO NOT FLUSH THE RX CHANNEL!
  crc = 0;
  ntbus_putc( b ); crc ^= b;
  ANGLETYPE axis;
  for(axis=PITCH;axis<=YAW;axis++){
    b = (u8)(vmax.a[axis]>>1) & 0x7F;
    ntbus_putc( b );  crc ^= b;
    uint16_t a = angle.a[axis];
    b = (u8)(a) & 0x7F; //low byte
    ntbus_putc( b );  crc ^= b;
    b = (u8)(a >> 7) & 0x7F; //high byte
    ntbus_putc( b );  crc ^= b;
  }
  ntbus_putc( crc & 0x7F );
}</nowiki>

Latest revision as of 21:29, 9 February 2016

The information on this page refers to firmware v0.86e and higher.

This page describes the protocol of the NT bus communication.

Hardware Details

The NT bus is nothing else than a standard TTL-UART, with parameters:

2.000.000 bps, 8 bits, 1 stop, no parity

The voltage levels are 3.3 V.

The Tx pin of any NT module connected to the bus must be in a high-impedance state in normal conditions. This line should be pulled to high on the main board side of the bus, ideally with a pull up resistor in the kilo-Ohm range.

Concept

The NT bus is designed as a master-slave network, with one master, the main STorM32 board, and up to 15 slaves, the NT modules. Any slave connected to the bus needs a unique ID. The ID has 4 bits. ID = 0 is however used to address all NT slaves on the bus at once.

The slaves are either Talker&Listener or only Listener. At any point in time only one slave is permitted to be talking, i.e. to send data to the master. Any slave may talk only in response to a command received by the master, a slave may not send any message on its own.

Currently these IDs are assigned:

#define NTBUS_ID_ALLMODULES               0
#define NTBUS_ID_IMU1                     1
#define NTBUS_ID_IMU2                     2
#define NTBUS_ID_MOTORALL                 3
#define NTBUS_ID_MOTORPITCH               4
#define NTBUS_ID_MOTORROLL                5
#define NTBUS_ID_MOTORYAW                 6
#define NTBUS_ID_LOGGER                   11

Communication Details

Communication from Master to Slaves

Every message emitted by the master begins with a start byte, which has three fields:

bit 7:        always 1, indicates that it is a start byte
bits 6,5,4:   short command
bits 3,2,1,0: ID of the NT module the message is for
#define NTBUS_STX                         0x80 // 0b 1000 0000
#define NTBUS_SFMASK                      0x70 // 0b 0111 0000
#define NTBUS_IDMASK                      0x0F // 0b 0000 1111

The following short commands are defined:

#define NTBUS_FLASH                       0x70 // 0b 0111 0000
#define NTBUS_RESET                       0x50 // 0b 0101 0000
#define NTBUS_SET                         0x40 // 0b 0100 0000
#define NTBUS_GET                         0x30 // 0b 0011 0000
#define NTBUS_TRIGGER                     0x10 // 0b 0001 0000
#define NTBUS_CMD                         0x00 // 0b 0000 0000

The start byte may be followed by further bytes, but these bytes must have their 7th bit set to low!

Transmitted data must be protected by a crc byte. It is calculated by a XOR over all data bytes, with the 7th bit set to low.

Examples:

  • The first message emitted by the master at the beginning of every new cycle is a group trigger, i.e.
0x90:  triggers all NT modules
  • When the data of the camera imu (IMU1) is requested by emitting
0xB1: get IMU1 data
  • Eventually the new motor angles will be send to all motor modules, which is done by addressing them all, i.e.
0xC3 + 10x motor data bytes + crc: set all motor to the new positions

Communication from Slave to Master

Whenever a NT module sends data to the STorM32, it first must enable it's Tx line, send the data, and then disable the Tx line.

In contrast to the messages from the master to slaves, full bytes can be send, i.e. the 7th bit doesn't have to be 0, since by design a slave never sends a command.

Error Handling

In the data stream from the master to the slaves only the start byte may have the 7th bit high. The data parser must reset on this occasion, which establishes the mechanism to return to proper operation if some error should have occurred.

In addition the receiving UARTs should check for error conditions offered by the hardware. For a STM32F103 this would be e.g. the overrun, noise, and frame flags.

Implementation Details

//STorM32 firmware v0.93
//9. Feb. 2015


// NT MODULE ID LIST
#define NTBUS_ID_ALLMODULES               0
#define NTBUS_ID_IMU1                     1
#define NTBUS_ID_IMU2                     2
#define NTBUS_ID_MOTORALL                 3
#define NTBUS_ID_MOTORPITCH               4
#define NTBUS_ID_MOTORROLL                5
#define NTBUS_ID_MOTORYAW                 6
#define NTBUS_ID_LOGGER                   11


// STARTBYTE flags and masks
#define NTBUS_STX                         0x80 // 0b 1000 0000
#define NTBUS_SFMASK                      0x70 // 0b 0111 0000
#define NTBUS_IDMASK                      0x0F // 0b 0000 1111

#define NTBUS_FLASH                       0x70 // 0b 0111 0000
#define NTBUS_RESET                       0x50 // 0b 0101 0000
#define NTBUS_SET                         0x40 // 0b 0100 0000
#define NTBUS_GET                         0x30 // 0b 0011 0000
#define NTBUS_TRIGGER                     0x10 // 0b 0001 0000
#define NTBUS_CMD                         0x00 // 0b 0000 0000


// COMMANDS , this is the command byte after a NTBUS_STX|NTBUS_CMD|NTBUS_ID startbyte
typedef enum {
  NTBUS_CMD_GETSTATUS = 1,
  NTBUS_CMD_GETVERSIONSTR,
  NTBUS_CMD_GETBOARDSTR,
  NTBUS_CMD_GETCONFIGURATION,

  NTBUS_CMD_ACCGYRO1RAWDATA_V1 = 32, //DEPRECTAED
  NTBUS_CMD_ACCGYRO2RAWDATA_V1,      //DEPRECTAED
  NTBUS_CMD_ACCGYRODATA_V1,          //DEPRECTAED
  NTBUS_CMD_PIDDATA, //35
  NTBUS_CMD_PARAMETERDATA, //36
  NTBUS_CMD_AHRS1DATA, //37
  NTBUS_CMD_AHRS2DATA,
  NTBUS_CMD_ACCGYRO3RAWDATA_V1, //39 //DEPRECATED
  NTBUS_CMD_ACCGYRO1RAWDATA_V2, //40
  NTBUS_CMD_ACCGYRO2RAWDATA_V2,
  NTBUS_CMD_ACCGYRO3RAWDATA_V2,
  NTBUS_CMD_ACCGYRO1DATA_V2, //43
  NTBUS_CMD_ACCGYRO2DATA_V2,

  NTBUS_CMD_READSETUP = 120,
  NTBUS_CMD_WRITESETUP,
  NTBUS_CMD_STORESETUP,

  NTBUS_CMD_NOTUSED = 0xFF
} NTBUSCMDTYPE;

// NTBUS_CMD_GETSTATUS
// from STorM32: stx, cmdbyte -> to STorM32: 2 x u8 cmddata, crc
PACKED(
typedef struct {
  char Status;
  uint8_t State;
}) tNTBusCmdGetStatusData;

#define NTBUS_CMDGETSTATUS_DATALEN        (sizeof(tNTBusCmdGetStatusData))

// NTBUS_CMD_GETVERSIONSTR
// from STorM32: stx, cmdbyte -> to STorM32: 16 x u8 cmddata, crc
PACKED(
typedef struct {
  char VersionStr[16];
}) tNTBusCmdGetVersionStrData;

#define NTBUS_CMDGETVERSIONSTR_DATALEN    (sizeof(tNTBusCmdGetVersionStrData))

// NTBUS_CMD_GETBOARDSTR
// from STorM32: stx, cmdbyte -> to STorM32: 16 x u8 cmddata, crc
PACKED(
typedef struct{
  char BoardStr[16];
}) tNTBusCmdGetBoardStrData;

#define NTBUS_CMDGETBOARDSTR_DATALEN      (sizeof(tNTBusCmdGetBoardStrData))

// NTBUS_CMD_GETCONFIGURATION
// from STorM32: stx, cmdbyte -> in STorM32: 1 x u16 cmddata, crc
PACKED(
typedef struct {
  uint16_t Configuration;
}) tNTBusCmdGetConfigurationData;

#define NTBUS_CMDGETCONFIGURATION_DATALEN (sizeof(tNTBusCmdGetConfigurationData))


// IMU MODULE
// SET: -
// GET: 3xi16 acc + 3xi16 gyro + 1xi16 temp + 1xu8 imu status

// status byte , is used for both internal bookkeeping and CMD command
#define NTBUS_IMU_STATUS_IMU_PRESENT      0x80

// configuration word
#define NTBUS_IMU_CONFIG_MPU6050          0x0001
#define NTBUS_IMU_CONFIG_MPU6000          0x0002
#define NTBUS_IMU_CONFIG_MPU9250          0x0004
#define NTBUS_IMU_CONFIG_MODELUNKNOWN     0x0000
#define NTBUS_IMU_CONFIG_MODELMASK        0x0007

#define NTBUS_IMU_CONFIG_I2C              0x0100
#define NTBUS_IMU_CONFIG_SPI          	  0x0200
#define NTBUS_IMU_CONFIG_BUSUNKNOWN       0x0000
#define NTBUS_IMU_CONFIG_BUSMASK          0x0300

// GET IMU imu status byte
#define NTBUS_IMU_IMUSTATUS_GYRODATA_OK   0x01
#define NTBUS_IMU_IMUSTATUS_ACCDATA_OK    0x02

// NTBUS_GET
// to STorM32: 6xi16 + 1xi16 + 1xu8 + crc = 15+1 bytes
PACKED(
typedef struct {
  int16_t AccX;     //+-2g
  int16_t AccY;     //+-2g
  int16_t AccZ;     //+-2g
  int16_t GyroX;    //+-1000°/s
  int16_t GyroY;    //+-1000°/s
  int16_t GyroZ;    //+-1000°/s
  int16_t Temp;
  uint8_t ImuStatus;
}) tNTBusGetImuData;

#define NTBUS_GETIMU_DATALEN              (sizeof(tNTBusGetImuData))


// MOTOR MODULE:
// SET: 1xu8 flags + 1xu8 vmax pitch + 1xi16 angle pitch + 1xu8 vmax roll + 1xi16 angle roll + 1xu8 vmax yaw + 1xi16 angle yaw
// GET: -

// configuration word
#define NTBUS_MOTOR_CONFIG_DEFAULT        0x0000

// enable flag byte
#define NTBUS_MOTOR_ENABLEFLAG_BEEP       0x40
#define NTBUS_MOTOR_ENABLEFLAG_GLOBAL     0x10 //this MUST agree with Motor flag
#define NTBUS_MOTOR_ENABLEFLAG_PITCH      0x01 //this MUST agree with Motor flag
#define NTBUS_MOTOR_ENABLEFLAG_ROLL       0x02 //this MUST agree with Motor flag
#define NTBUS_MOTOR_ENABLEFLAG_YAW        0x04 //this MUST agree with Motor flag

// NTBUS_SET
// from STorM32: data + crc = 10+1 bytes
// it has it's "own" way to ensure that the 7th bits are zero
PACKED(
typedef struct {
  uint8_t Flags;
  uint8_t VmaxPitch;
  int16_t AnglePitch;
  uint8_t VmaxRoll;
  int16_t AngleRoll;
  uint8_t VmaxYaw;
  int16_t AngleYaw;
}) tNTBusSetMotorAllData;

#define NTBUS_SETMOTORALL_DATALEN         (sizeof(tNTBusSetMotorAllData))


// LOGGER MODULE:
// SET: 1xu32 + 7xu8 + 5xu16 + 6xi16
// GET: -

// configuration word
#define NTBUS_LOGGER_CONFIG_DEFAULT       0x0000

// SET LOGGER imu status byte
#define NTBUS_LOGGER_IMUSTATUS_GYRODATA_OK    NTBUS_IMU_IMUSTATUS_GYRODATA_OK //0x01
#define NTBUS_LOGGER_IMUSTATUS_ACCDATA_OK     NTBUS_IMU_IMUSTATUS_ACCDATA_OK //0x02

// NTBUS_SET
// from STorM32: 1xu32 + 7xu8 + 5xu16 + 6xi16 = 33 bytes
PACKED(
typedef struct {
  uint32_t TimeStamp32; // in us
  uint8_t Imu1received; // in 10us units
  uint8_t Imu1done;     // in 10us units
  uint8_t PIDdone;      // in 10us units
  uint8_t Motorsdone;   // in 10us units
  uint8_t Imu2received; // in 10us units
  uint8_t Imu2done;     // in 10us units
  uint8_t Loopdone;     // in 10us units

  uint16_t State;
  uint16_t Status;
  uint16_t Status2;
  uint16_t ErrorCnt;
  uint16_t Voltage;

  int16_t Imu1AnglePitch;
  int16_t Imu1AngleRoll;
  int16_t Imu1AngleYaw;
  int16_t Imu2AnglePitch;
  int16_t Imu2AngleRoll;
  int16_t Imu2AngleYaw;
}) tNTBusSetLoggerData;

#define NTBUS_SETLOGGER_DATALEN           (sizeof(tNTBusSetLoggerData))
#define NTBUS_SETLOGGER_HIGHBITSLEN       6 //33 bytes => 5 bytes required => 3xu16 = 6 bytes used

/* DEPRECATED VERSION1
// NTBUS_CMD_ACCGYRO1RAWDATA, NTBUS_CMD_ACCGYRO2RAWDATA
// from STorM32: 6xi16 = 12 bytes
PACKED(
typedef struct {
  int16_t axraw;
  int16_t ayraw;
  int16_t azraw;
  int16_t gxraw;
  int16_t gyraw;
  int16_t gzraw;
}) tNTBusCmdAccGyroRawDataV1;

#define NTBUS_CMDACCGYRORAWDATAV1_DATALEN       (sizeof(tNTBusCmdAccGyroRawDataV1))
#define NTBUS_CMDACCGYRORAWDATAV1_HIGHBITSLEN   2 //12 bytes => 2 bytes required => 1xu16 = 2 bytes used */

// NTBUS_CMD_ACCGYRO1RAWDATA, NTBUS_CMD_ACCGYRO2RAWDATA V2
// from STorM32: 7xi16 = 14 bytes
PACKED(
typedef struct {
  int16_t axraw;
  int16_t ayraw;
  int16_t azraw;
  int16_t gxraw;
  int16_t gyraw;
  int16_t gzraw;
  int16_t temp;
}) tNTBusCmdAccGyroRawDataV2;

#define NTBUS_CMDACCGYRORAWDATAV2_DATALEN     (sizeof(tNTBusCmdAccGyroRawDataV2))
#define NTBUS_CMDACCGYRORAWDATAV2_HIGHBITSLEN 2 //14 bytes => 2 bytes required => 1xu16 = 2 bytes used

/* DEPRECATED VERSION1
// NTBUS_CMD_ACCGYRODATA
// from STorM32: 7xi16 + 1xu8 + 7xi16 + 1xu8 = 30 bytes
PACKED(
typedef struct {
  int16_t ax1;
  int16_t ay1;
  int16_t az1;
  int16_t gx1;
  int16_t gy1;
  int16_t gz1;
  int16_t temp1;
  uint8_t ImuState1;

  int16_t ax2;
  int16_t ay2;
  int16_t az2;
  int16_t gx2;
  int16_t gy2;
  int16_t gz2;
  int16_t temp2;
  uint8_t ImuState2;
}) tNTBusCmdAccGyroDataV1;

#define NTBUS_CMDACCGYRODATAV1_DATALEN      (sizeof(tNTBusCmdAccGyroDataV1))
#define NTBUS_CMDACCGYRODATAV1_HIGHBITSLEN  6 //30 bytes => 5 bytes required => 3xu16 = 6 bytes used */

// NTBUS_CMD_ACCGYRODATA VERSION2
// from STorM32: 6xi16 + 1xu8 = 13 bytes
PACKED(
typedef struct {
  int16_t ax;
  int16_t ay;
  int16_t az;
  int16_t gx;
  int16_t gy;
  int16_t gz;
  uint8_t ImuState;
}) tNTBusCmdAccGyroDataV2;

#define NTBUS_CMDACCGYRODATAV2_DATALEN      (sizeof(tNTBusCmdAccGyroDataV2))
#define NTBUS_CMDACCGYRODATAV2_HIGHBITSLEN  2 //13 bytes => 2 bytes required => 1xu16 = 2 bytes used

// NTBUS_CMD_PIDDATA
// from STorM32: 6xi16 = 12 bytes
PACKED(
typedef struct {
  int16_t PIDCntrlPitch;
  int16_t PIDCntrlRoll;
  int16_t PIDCntrlYaw;
  int16_t PIDMotorCntrlPitch;
  int16_t PIDMotorCntrlRoll;
  int16_t PIDMotorCntrlYaw;
}) tNTBusCmdPidData;

#define NTBUS_CMDPIDDATA_DATALEN          (sizeof(tNTBusCmdPidData))
#define NTBUS_CMDPIDDATA_HIGHBITSLEN      2 //12 bytes => 2 bytes required => 1xu16 = 2 bytes used

// NTBUS_CMD_PARAMETERDATA
// from STorM32: 3xi16 + 16xu8 = 22 bytes
PACKED(
typedef struct{
  uint16_t Adr;
  uint16_t Value;
  uint16_t Format;
  char NameStr[16];
}) tNTBusCmdParameterData;

#define NTBUS_CMDPARAMETERDATA_DATALEN    (sizeof(tNTBusCmdParameterData))
#define NTBUS_CMDPARAMETERDATA_HIGHBITSLEN  4 //22 bytes => 4 bytes required => 2xu16 = 4 bytes used

// NTBUS_CMD_AHRSDATA
// from STorM32: 5xi16 = 10 bytes
PACKED(
typedef struct {
  int16_t rx;
  int16_t ry;
  int16_t rz;
  int16_t accconfidence;
  int16_t yawtarget;
}) tNTBusCmdAhrsData;

#define NTBUS_CMDAHRSDATA_DATALEN         (sizeof(tNTBusCmdAhrsData))
#define NTBUS_CMDAHRSDATA_HIGHBITSLEN     2 //10 bytes => 2 bytes required => 1xu16 = 2 bytes used



// IMU MODULE SPECIFIC routines

// try to detect module, 10 times max
// returns 0 or 0x01
// executed by STorM32
uint16_t ntbus_find_module(uint8_t id)
{
uint16_t i, ret;

  for( i=0; i<10; i++ ){
	ntbus_putcmd( id, NTBUS_CMD_GETSTATUS );
    ret = ntbus_get( NTBUS_CMDGETSTATUS_DATALEN+1, 50 ); //wait only for 50 us
    if( ret ) return 1;
  }
  return 0;
}


// initialize imu module when it had been found
// returns 1 or 0x03
// executed by STorM32
uint16_t ntbus_ready_imumodule(uint8_t id)
{
uint16_t i, ret;

  for(i=0; i<100; i++){
    ntbus_putcmd( id, NTBUS_CMD_GETSTATUS );
    ret = ntbus_get( NTBUS_CMDGETSTATUS_DATALEN+1, 50 ); //wait only for 50 us
    if( ret &&( ntbus_buf[0] == NTBUS_IMU_STATUS_IMU_PRESENT )&&( ntbus_buf[1] == NTBUS_STATE_READY )){
      //read it once to startup
      ntbus_putsf_wflushall( id, NTBUS_TRIGGER );
      ntbus_putsf( id, NTBUS_GET );
      ntbus_get( NTBUS_GETIMU_DATALEN+1, 500 );
      return 3;
    }
    delay_ms(50);
  }

  return 1;
}


// send from imu module to STorM32
void ntbus_putimudata(void *imu, uint8_t imustatus)
{
uint8_t i; uint8_t crc; char c;

  crc = 0;
  for( i=0; i<NTBUS_GETIMU_DATALEN-1; i++ ){ c = *((u8*)(imu++)); ntbus_putc( c ); crc ^= c; }
  c = imustatus;
  ntbus_putc( c ); crc ^= c;
  ntbus_putc( crc );
}


// MOTOR MODULE SPECIFIC routines

// send from STorM32 to all motor modules
void ntbus_setmotoralldata(uint16_t enabledflag, uint16_t beep, tu16Angle vmax, tu16Angle angle)
{
uint8_t b, crc;

  b = 0;
  if( enabledflag & MOTORGLOBALENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_GLOBAL;
  if( enabledflag & MOTORPITCHENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_PITCH;
  if( enabledflag & MOTORROLLENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_ROLL;
  if( enabledflag & MOTORYAWENABLED ) b |= NTBUS_MOTOR_ENABLEFLAG_YAW;
  if( beep ) b |= NTBUS_MOTOR_ENABLEFLAG_BEEP;

  ntbus_putsf( NTBUS_ID_MOTORALL, NTBUS_SET ); //DO NOT FLUSH THE RX CHANNEL!
  crc = 0;

  ntbus_putc( b ); crc ^= b;
  ANGLETYPE axis;
  for(axis=PITCH;axis<=YAW;axis++){
    b = (u8)(vmax.a[axis]>>1) & 0x7F;
    ntbus_putc( b );  crc ^= b;
    uint16_t a = angle.a[axis];
    b = (u8)(a) & 0x7F; //low byte
    ntbus_putc( b );  crc ^= b;
    b = (u8)(a >> 7) & 0x7F; //high byte
    ntbus_putc( b );  crc ^= b;
  }

  ntbus_putc( crc & 0x7F );
}