DAMI-M3 master_sensor_mod2_v15

 

/*
* 
* DAMI-M3
* 
* master-sensor-mod2-vx
* 
* LIBRARIES
* Wire (Arduino I2C Master)
* Servo
* SparkFun VL53L1X 4M Laser Distance Sensor
* newPing
* kalman simple (local)
* SPI
* RF24
* nRF24L01
* 
*/
/*
O rascunho usa 15626 bytes (50%) do espaço de armazenamento do programa. O máximo é 30720 bytes.
Variáveis globais usam 1400 bytes (68%) de memória dinâmica
*/

#define IDLE_LOOP_COUNTER_MAX 5

#define SIDE_OFFSET 60
//#define FRONT_OFFSET 130
//#define REAR_OFFSET 60

#define FRONT_LASER_MAX_DISTANCE 3000

//#define FRONT_LASER_FRONT_OFFSET 120;
//#define FRONT_LASER_SIDE_OFFSET 0;

//#define SIDE_SONAR_FRONT_OFFSET 85;
//#define SIDE_SONAR_SIDE_OFFSET 60;

#define WALL_SIDE_THRESHOLD 100
#define WALL_SIDE_ROTATE_THRESHOLD 170
#define WALL_FRONT_THRESHOLD 200
#define WALL_FRONT_ALLEY_THRESHOLD 500

union u_tag {
byte b[4];
int i[2];
double d;
long l;
};

// BOF: nRF240L01
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

#define CE_PIN 9
#define CSN_PIN 10

const byte slaveAddress[5] = {'R','x','A','A','A'};

RF24 radio(CE_PIN, CSN_PIN); // Create a Radio

//char dataToSend[10] = "Message 0";
//char txNum = '0';
int rfAckData[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // to hold the two values coming from the slave
byte rfSendDataStatus = 0;

union u_tag rfSendData[8];
// EOF: nRF240L01

#include <Servo.h>
Servo frontServo;
#define READ_POSITION_MAX 13
byte readPosition[READ_POSITION_MAX] = {0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180};
int readPositionDistance[READ_POSITION_MAX] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

#define frontServoPin 5

#define outputForPlotter false
#define serialSpeed 115200

// BOF: BATTERY VOLTAGE
// BAT 12V, R1=1.8K, R2=1.2K AoutMAx=883 # A0 * (12.00 / 883.00)
#define BATERY_VOLTAGE 12.00
#define BATERY_MAX_OUT 883
float batteryRead;
// EOF: BATTERY VOLTAGE

#include <Wire.h>
// arduino slaves i2c address array
byte i2cSlave[1] = {8};
// I2C byte conversion
union u_tag i2c_data[1][16];

// BOF: SparkFun VL53L1X 4M Laser Distance Sensor
#include "SparkFun_VL53L1X_Arduino_Library.h"
VL53L1X distanceSensor;
int bodyFrontLaserRead;
// EOF: SparkFun VL53L1X 4M Laser Distance Sensor

// BOF HC-SR04
#include <NewPing.h>
#define SONAR_NUM 2 // Number of sensors.
#define SONAR_MAX_DISTANCE 200 // Maximum distance (in cm) to ping.
#define PING_WAIT_TIME = 33; // should be more than 29ms

NewPing bodySonar[SONAR_NUM] = { // Sensor object array.
NewPing(4, 4, SONAR_MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping. 
NewPing(7, 7, SONAR_MAX_DISTANCE), 
};
int bodySonarRead[SONAR_NUM];
byte bodySonarIndex = 0; // Holds the current sonar for loop
// EOF HC-SR04

// BOF: KALMAN
#include "Kalman.h";
// kalman
int bodyFrontLaserDistance;
boolean bodyFrontLaserDistanceUpdate = true;
int bodyFrontLaserDistanceQuick;
int bodySonarDistance[SONAR_NUM];
float batteryVoltage;
Kalman batteryKalman(0.125,32,1023,0);
//Kalman bodyFrontLaserKalman(0.125,32,1023,1000);
Kalman bodyFrontLaserKalman(0.125,0.25,1023,1000);
//Kalman bodyFrontLaserKalmanQuick(0.125,0.25,1023,1000);
Kalman bodySonarKalman0(1,0.25,1023,100);
Kalman bodySonarKalman1(1,0.25,1023,100);
// EOF: KALMAN

#define LED_PIN 8 
bool internalLedBlinkState = false;

// TODO redefine with internal defines
#ifndef PI2
#define PI2 6.28318530718
#endif

#define BODY_WHEEL_RADIUS 52
#define BODY_WHEEL_DISTANCE 120
#define BODY_WHEEL_RESOLUTION 4096 // 4096 / 2
#define BODY_MOTOR_ACCELERATION 25 // steps/sec
#define BODY_MOTOR_SPEEDBASE 250
#define BODY_MOTOR_SPEEDTOP 1000

float bodyMotorStepsPerUnit = 0;
//signed long bodyMotorUnitsTarget;
signed long bodyMotorStepsTarget;
signed long bodyMotorLeftStepsToGo;
signed long bodyMotorLeftSteps;
signed long bodyMotorLeftStepsLast;
//signed long bodyMotorRightStepsToGo;

// odometry
double bodyThetaRadians = 0;
double bodyPosX = 0;
double bodyPosY = 0;

byte slaveCmdRequestId = 0;
byte slaveCmdWaitingId = 0;
byte slaveCmdReceivedId = 0;
byte slaveCmdTerminateId = 0;

byte cmd;
// commands sequence
#define TMP_PRG_OFFSET 2
#define PRG_CMD_MAX 10 // 10 = 8 cmd + 2 reserved positions
typedef struct {
byte extId;
byte group;
byte cmd;
byte intCmd;
byte extCmd; // 1=stop, 2=line, 3=rotate
signed long units; // distance in mm/cm (depends on resolution), angles on degrees
int minSpeed; 
int acceleration;
int maxSpeed; 
} cmd_t;

/*
cmd_t prgCmd[PRG_CMD_MAX] = {
{0, 0, 2, 0, 2, 100, 250, 50, 1500}, {0, 0, 3, 0, 3, 90, 250, 50, 1500}, {0, 0, 2, 0, 2, -100, 250, 50, 1500}, 
{0, 0, 2, 0, 3, -90, 250, 50, 1500}, {0, 0, 253, 253, 0, 0, 0, 0, 0}
};
*/
cmd_t prgCmd[PRG_CMD_MAX] = {
{0, 0, 2, 21, 2, 5000, 250, 50, 1500}, {0, 0, 3, 0, 3, 90, 250, 50, 1500}, {0, 0, 2, 0, 2, 250, 250, 50, 1500}, 
{0, 0, 2, 0, 3, 90, 250, 50, 1500}, {0, 0, 253, 253, 0, 0, 0, 0, 0}
};


byte prgIdx = 0;
byte bodyMoveProcessStatus = 0;
boolean internalCmdProcessStatus = false;

//byte bodyMoveInternalCommand = 0;
byte currentCommandGroupId = 0;
byte commandGroupId = 0;
//byte bodyMoveCommandLastGroup = 0;

byte internalCmdStepCount = 0;
unsigned long internalCmdLoopCountNext = 0;

unsigned int internalMoveLoopCounter = 0;
unsigned int bodyMoveLoopCounter = 0;

#define BODY_MOVE_LOG_MAX 24
typedef struct {
byte cmd; // external Command
signed long units; // distance in mm/cm (depends on resolution), angles on degrees
} moveLog_t;
moveLog_t bodyMoveLog[BODY_MOVE_LOG_MAX];
byte bodyMoveNextLog = 0;

boolean rotateScanRequest;
boolean rotateScanStatus;

byte frontObstacleDetectedStatus = 0;

byte loopTime;
unsigned long loopTimeStart;

byte idleLoopCounter = 0;

#define MAP_L0_MAX 10
#define MAP_L0_RES 500 // BASE MAP CELL in mm
#define MAP_L0_XOFFSET 5 // offset to 0,0
#define MAP_L0_YOFFSET 5 // offset to 0,0
#define MAP_L1_MAX 100 // (= MAP_L0_MAX * MAP_L0_MAX)
#define MAP_L1_RES 50 // SECUNDARY MAP CELL in mm (= MAP_L0_RES / (MAP_L1_MAX / MAP_L0_MAX))
#define MAP_L1_XOFFSET 50 // offset to 0,0
#define MAP_L1_YOFFSET 50

struct s_cellSummary {
byte hit;
byte miss;
};

struct s_cellSummary mapSummary[10][10];
//struct s_cellSummary cellSummary[10][10];

cmd_t receivedCmd;
//boolean ackReceivedCmd = false;
byte prgIdxPaused = 0;
signed long pausedUnits = 0;
byte pausedCmd = 0;

// reset function
void (* resetFunc) (void) = 0;

void setup() {
static unsigned long internalLedTimeStart = millis() + 250;
int i;

int j;
for(j=0; j < MAP_L0_MAX; j++) 
for(i=0; i < MAP_L0_MAX; i++)
mapSummary[j][i] = {0, 0};

pinMode(LED_PIN, OUTPUT); // configure internal LED for output

Serial.begin(serialSpeed); // start serial for output
//while (!Serial);

Wire.begin(); // join i2c bus (address optional for master)
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties

// set constant vars
bodyMotorStepsPerUnit = (float)((float)(BODY_WHEEL_RADIUS * PI2) / BODY_WHEEL_RESOLUTION);

// reset slave using i2c master writer
Wire.beginTransmission(i2cSlave[0]); 
Wire.write(254); // cmd
Wire.write(slaveCmdRequestId); // cmdId
Wire.endTransmission();
// does not update cmdId

// BOF: nRF240L01
radio.begin();
radio.setDataRate( RF24_250KBPS );
radio.enableAckPayload();
radio.setRetries(3,5); // delay, count
radio.openWritingPipe(slaveAddress);
// EOF: nRF240L01

// init VL53L1X 4M Laser Distance Sensor
if (!distanceSensor.begin()) {
Serial.println("Sensor VL53L1X offline. Check wiring!");
while (1);
}

distanceSensorsAdaptativePeriod(45);

// wait some time for the slave restart
delay(1000);

// config basic slave vars using i2c master writer
Wire.beginTransmission(i2cSlave[0]); 
Wire.write(90); // cmd
Wire.write(slaveCmdRequestId); // cmdId
Wire.write(BODY_WHEEL_RADIUS); Wire.write(BODY_WHEEL_DISTANCE);
i=BODY_WHEEL_RESOLUTION; Wire.write((byte*)&i, 2); // units, deg
i=BODY_MOTOR_ACCELERATION; Wire.write((byte*)&i, 2); // minSpeed
i=BODY_MOTOR_SPEEDBASE; Wire.write((byte*)&i, 2); // aceleration
i=BODY_MOTOR_SPEEDTOP; Wire.write((byte*)&i, 2); // maxSpeed
Wire.endTransmission();
// does not update cmdId

// get update from slave only
getSlavesUpdate();

// servo 
frontServo.attach(frontServoPin);
//control the servo's direction and the position of the motor
delay(50); 
frontServo.write(0);
delay(500);
frontServo.write(90);
delay(500);
frontServo.detach();
delay(50);

// extra delay for VL53L1X stability
delay(500);
/*
// wait for start & do some previous readings
//Serial.println("Wait for hands on sides!");
while((bodySonarDistance[0] < 1 ) || (bodySonarDistance[1] < 1) ||
(bodySonarDistance[0] - SIDE_OFFSET > WALL_SIDE_THRESHOLD) || (bodySonarDistance[1] - SIDE_OFFSET > WALL_SIDE_THRESHOLD)
) {
readSonarDistance();
readFrontLaserDistance();
// blink LED to indicate activity
if(millis() >= internalLedTimeStart) {
internalLedTimeStart = millis() + 250;
internalLedBlinkState = !internalLedBlinkState;
digitalWrite(LED_PIN, internalLedBlinkState);
}
}
*/
distanceSensorsAdaptativePeriod(30);

// lets start looping 
loopTimeStart = millis();
}

void loop() { 
static unsigned long internalLedTimeStart = millis() + 1000;
int i;

cmdProcess();
processInternalOnlyCommands();
processInternalSlaveCommands();
readSensors();

// detect obstacles moving front
if(prgCmd[prgIdx].extCmd == 2 && !frontObstacleDetectedStatus) {
// check front distance
//if(bodyFrontLaserDistance - FRONT_OFFSET < WALL_FRONT_THRESHOLD) {
i = getFrontLaserDistance(2); // bodyFrontLaserDistance HARDECODED distance mode
if(i > 0 && i < WALL_FRONT_THRESHOLD) {
frontObstacleDetectedStatus = 1;
//Serial.print("SENDSTOP"); Serial.println("\t");
}
// check front distance both sides 45 degrees 
if(prgCmd[prgIdx].intCmd == 21 && !frontObstacleDetectedStatus) {
// check left
i = readPositionDistance[3]; // HARDECODED 
if(i > 0 && i < WALL_FRONT_THRESHOLD) {
frontObstacleDetectedStatus = 1;
}
// check right
i = readPositionDistance[9]; // HARDECODED
if(i > 0 && i < WALL_FRONT_THRESHOLD) {
frontObstacleDetectedStatus = 1;
}
}
}

//currentMillis = millis();
//if (currentMillis - prevMillis >= txIntervalMillis) {
sendRadioLoopData();
//}

// processing incomming radio data 
if (rfSendDataStatus == 2) {
// incomming remote data

// check for a cmd
if(rfAckData[0] == 0 && rfAckData[1]) { // HARDCODED: TODO: change == 0 to == 1
// there is a command to process
if(!receivedCmd.cmd) {
receivedCmd.cmd = rfAckData[1];
// process imediate commands
switch(receivedCmd.cmd) { 
case 254: // reset
resetFunc();
break;
}
// the command will be processed on next loop
//ackReceivedCmd = true;
//} else {
//ackReceivedCmd = false;
}
}
}

loopTime = millis() - loopTimeStart;
loopTimeStart = millis();

sendSerialLoopData();

// update internal loop counter
if(internalCmdProcessStatus) internalMoveLoopCounter++;

// blink LED to indicate activity
if(millis() >= internalLedTimeStart) {
internalLedTimeStart = millis() + 1000;
internalLedBlinkState = !internalLedBlinkState;
digitalWrite(LED_PIN, internalLedBlinkState);
}

delay(15);
}

//
//
// BOF CINEMATIC CONTROL
//
//

void bodyMoveDecide() {
int i;
int maxDistance = 0;
byte maxDistanceIdx = 0;

byte emptyIndex[READ_POSITION_MAX];
byte emptyIndexIdx = 0;

boolean needToGoBackward = false;

//byte selectedIdx;
float theta;

int j;
float xpos, ypos;
int xidx, yidx;
float beta;
unsigned int memAddress;

// debug
//Serial.print("READINGS"); Serial.println("\t");
for(i = 0; i < READ_POSITION_MAX; i++) {

Serial.print(readPosition[i]); Serial.print("\t");
Serial.print(readPositionDistance[i]); Serial.println("\t");

beta = bodyThetaRadians + (float)(readPosition[i] / 180.0) * PI;
xpos = readPositionDistance[i] * cos(beta);
ypos = readPositionDistance[i] * sin(beta);

/*
Serial.print(xpos); Serial.print("\t");
Serial.print(ypos); Serial.print("\t");
*/
// low resolution
xidx = (int)(xpos/MAP_L0_RES) + MAP_L0_XOFFSET;
yidx = (int)(ypos/MAP_L0_RES) + MAP_L0_YOFFSET;

// hits 
//mapSummary[(int)(xpos/MAP_L0_RES) + MAP_L0_XOFFSET][(int)(ypos/MAP_L0_RES) + MAP_L0_YOFFSET].hit++;
if( xidx >= 0 && xidx < MAP_L0_MAX && yidx >= 0 && yidx < MAP_L0_MAX) {
mapSummary[xidx][yidx].hit++;
}

// index on small map cell / just for memory
//xidx = (int)(fmod(xpos, MAP_L0_RES) * MAP_L1_K1 / MAP_L1_K2) + MAP_L0_XOFFSET;
//yidx = (int)(fmod(ypos, MAP_L0_RES) * MAP_L1_K1 / MAP_L1_K2) + MAP_L0_XOFFSET; 
//Serial.print(xidx); Serial.print("\t");
//Serial.print(yidx); Serial.print("\t");

// middle resolution
xidx = (int)(xpos/MAP_L1_RES) + MAP_L1_XOFFSET;
yidx = (int)(ypos/MAP_L1_RES) + MAP_L1_YOFFSET;
//Serial.print(xidx); Serial.print("\t");
//Serial.print(yidx); Serial.print("\t");
memAddress = xidx * MAP_L1_MAX + yidx;
//Serial.print(memAddress); Serial.print("\t");
//Serial.print(memAddress); Serial.print("\t");
//if( xidx >= 0 && xidx < MAP_L1_MAX && yidx >= 0 && yidx < MAP_L1_MAX) {
// 
//}

// miss 
//Serial.print("miss\t");
for(j = 4000; j > 0; j -= MAP_L0_RES) { // HARDCODED: MAX SENSOR READ DISTANCE
if((j) < readPositionDistance[i]) {
// para cá da distancia
xpos = j * cos(beta);
ypos = j * sin(beta);
xidx = (int)(xpos/MAP_L0_RES) + MAP_L0_XOFFSET;
yidx = (int)(ypos/MAP_L0_RES) + MAP_L0_YOFFSET;
if( xidx >= 0 && xidx < MAP_L0_MAX && yidx >= 0 && yidx < MAP_L0_MAX) {
//mapSummary[(int)(xpos/MAP_L0_RES) + MAP_L0_XOFFSET][(int)(ypos/MAP_L0_RES) + MAP_L0_YOFFSET].miss++;
mapSummary[xidx][yidx].miss++;
}

xidx = (int)(xpos/MAP_L1_RES) + MAP_L1_XOFFSET;
yidx = (int)(ypos/MAP_L1_RES) + MAP_L1_YOFFSET; 
//Serial.print(xidx); Serial.print("\t");
//Serial.print(yidx); Serial.print("\t");
memAddress = xidx * MAP_L1_MAX + yidx;
//Serial.print(memAddress); Serial.print("\t");
//if( xidx >= 0 && xidx < MAP_L1_MAX && yidx >= 0 && yidx < MAP_L1_MAX) {
// 
//}

/*
Serial.print(j); Serial.print("\t");
Serial.print(xidx); Serial.print("\t");
Serial.print(yidx); Serial.print("\t");
Serial.print((int)(xpos/MAP_L0_RES) + MAP_L0_XOFFSET); Serial.print("\t");
Serial.print((int)(ypos/MAP_L0_RES) + MAP_L0_YOFFSET); Serial.print("\t");
Serial.print(xpos); Serial.print("\t");
Serial.print(ypos); Serial.print("\t");
*/

} else {
// para alem da distancia

}
}

//Serial.println("");
// update max distance found
if(readPositionDistance[i] > maxDistance) {
maxDistance = readPositionDistance[i];
maxDistanceIdx = i;
}
// update unknow distance array
if(readPositionDistance[i] == 0) {
emptyIndex[emptyIndexIdx] = i;
emptyIndexIdx++;
}
}

// check if we can rotate
// TODO: calculate the max allowed degrees to rotate in available space

boolean canRotateLeft = true;
boolean canRotateRight = true;
if(((bodySonarDistance[0] > 0) && (bodySonarDistance[0] < WALL_SIDE_ROTATE_THRESHOLD)) || ((bodySonarDistance[1] > 0) && (bodySonarDistance[1] < WALL_SIDE_THRESHOLD))) {
canRotateLeft = false;
}
if(((bodySonarDistance[1] > 0) && (bodySonarDistance[1] < WALL_SIDE_ROTATE_THRESHOLD)) || ((bodySonarDistance[0] > 0) && (bodySonarDistance[0] < WALL_SIDE_THRESHOLD))) {
canRotateRight = false;
}

// we have done a pre selection of best possible directions
// the ones we have more space lets do a final choose

if(!emptyIndexIdx) {
//
// he have no unknow distance readings
//
if(!canRotateLeft && !canRotateRight && readPositionDistance[maxDistanceIdx] < WALL_FRONT_ALLEY_THRESHOLD) {
// we are in a alley, and cant rotate.
needToGoBackward = true;
theta = 180;
//selectedIdx = READ_POSITION_MAX;
Serial.println("SELECT GoBackward ");
} else {
if(readPositionDistance[maxDistanceIdx] < WALL_FRONT_ALLEY_THRESHOLD) {
// we are in a alley and can rotate
if(canRotateLeft) {
// rotate 180 degrees left
theta = 180;
Serial.println("SELECT rotate 180");
} else {
// rotate 180 degrees right
theta = -180;
Serial.println("SELECT rotate -180");
}
//selectedIdx = READ_POSITION_MAX; 
} else {
// be stupid, select the max distance
theta = readPosition[maxDistanceIdx] - 90;
//selectedIdx = maxDistanceIdx;
Serial.print("SELECT MAX ");
Serial.print(readPosition[maxDistanceIdx]); Serial.print("\t");
//Serial.print(readPositionDistance[maxDistanceIdx]); Serial.print("\t");
Serial.print(theta); Serial.println("\t"); 
}

} 
} else {
// we have unknow distance readings
// must select one from the available
// be stupid, select the first
theta = readPosition[emptyIndex[0]] - 90;
//selectedIdx = emptyIndex[0];
Serial.print("SELECT empty ");
Serial.print(readPosition[emptyIndex[0]]); Serial.print("\t");
//Serial.print(readPositionDistance[0]); Serial.print("\t");
Serial.print(theta); Serial.println("\t");
}

/*
Serial.print("HIT MAP"); Serial.println("\t");
for(j=0; j < 10; j++) {
for(i=0; i < 10; i++) {
Serial.print(mapSummary[j][i].hit); Serial.print("\t");
}
Serial.println();
}

Serial.print("MISS MAP"); Serial.println("\t");
for(j=0; j < 10; j++) {
for(i=0; i < 10; i++) {
Serial.print(mapSummary[j][i].miss); Serial.print("\t");
}
Serial.println();
}
*/

sendRadioServoScanData(readPosition[(int)theta]);

// dismiss reaction to a front obstacle (not check is setted)
frontObstacleDetectedStatus = 0;

// set new commands
if(needToGoBackward) {
prgCmd[0] = {0, 0, 2, 0, 2, -500, 0, 0, 0}; // HARDCODED commands
} else {
prgCmd[0] = {0, 0, 3, 0, 3, theta, 0, 0, 0}; // HARDCODED commands
prgCmd[1] = {0, 0, 2, 0, 2, 500, 0, 0, 0}; // HARDCODED commands 
}
}

void processInternalOnlyCommands() {
static boolean cmdStepStatus;

// internal behaviors with slave stopped
if(internalCmdProcessStatus && !slaveCmdWaitingId) { // ADD? && !slaveCmdTerminateId
switch(prgCmd[prgIdx].intCmd) {
//
// servo sweep behavior to get 180 degrees distances readings
//
case 150: // HARDCODED
// start process command 150 = servo scan
if(internalCmdLoopCountNext == 0) {
//Serial.print("Start sweep"); Serial.println("\t");
internalCmdLoopCountNext = internalMoveLoopCounter + 10; // TODO HARDCODED
internalCmdStepCount = 0;
cmdStepStatus = false;
readPositionDistance[0] = bodySonarDistance[1];
readPositionDistance[READ_POSITION_MAX - 1] = bodySonarDistance[0];
// servo
frontServo.attach(frontServoPin);
delay(50);
bodyFrontLaserDistanceUpdate = false;
}

if(internalCmdLoopCountNext == internalMoveLoopCounter) {
readPositionDistance[internalCmdStepCount+1] = getFrontLaserDistance(1); // TODO HARDCODED distance mode
internalCmdLoopCountNext = internalMoveLoopCounter + 10; // TODO HARDCODED
internalCmdStepCount++;
cmdStepStatus = false;
}

if(internalCmdStepCount == prgCmd[prgIdx].units) { 
// move servo to front, detach and end
frontServo.write(90); // TODO: HARDCODED
delay(100);
frontServo.detach();
delay(50);
cmdEnd();
cmdStepStatus = false;
bodyFrontLaserDistanceUpdate = true;
bodyMoveDecide(); 
}

if(!cmdStepStatus) {
// move servo to some angle in position table
delay(100);
frontServo.write(readPosition[internalCmdStepCount]);
delay(200);
cmdStepStatus = true;
}
break;

//
case 251: // HARDCODED
// goto prgIdx command
case 252: // HARDCODED
// pause the program counter (keep executing pause)
case 253: // HARDCODED
// restart the program counter (keep program)
cmdEnd();
break;
}
}
}

void processInternalSlaveCommands() {
static boolean cmdStepStatus;
static unsigned long smallSweepNextTimer = 0;
static boolean cmdTimerStatus = false; // TODO?: rewrite to spare memory

if(internalCmdProcessStatus && slaveCmdWaitingId) {
// TODO: rotate scan

switch(prgCmd[prgIdx].intCmd) {
//
// servo sweep behavior to get 90 degrees wide distances readings
//
case 21: // HARDCODED
if(millis() > smallSweepNextTimer) {
smallSweepNextTimer = millis() + 10000; // TODO HARDCODED
cmdTimerStatus = true;
}
if(cmdTimerStatus) {
// start operation
if(internalCmdLoopCountNext == 0) {
//Serial.println("Start short sweep"); Serial.println("\t");
internalCmdLoopCountNext = internalMoveLoopCounter + 20; // TODO HARDCODED
internalCmdStepCount = 0;
cmdStepStatus = false;
// servo
frontServo.attach(frontServoPin);
delay(5);
bodyFrontLaserDistanceUpdate = false;
}
// do sensor readings 
if(internalCmdLoopCountNext == internalMoveLoopCounter) {
readPositionDistance[ 3 + (internalCmdStepCount * 3)] = getFrontLaserDistance(1); // TODO HARDCODED start position, increment, distance mode
// update stall front laser distance
if(internalCmdStepCount == 1) {
bodyFrontLaserDistance = readPositionDistance[ 3 + (internalCmdStepCount * 3)];
}
// update control vars
internalCmdLoopCountNext = internalMoveLoopCounter + 20; // TODO HARDCODED
internalCmdStepCount++;
cmdStepStatus = false;
} 
// end operation: move servo to front, detach and end
if(internalCmdStepCount == 3) {
frontServo.write(90); // TODO: HARDCODED
delay(100);
frontServo.detach();
delay(50);
internalCmdLoopCountNext = 0;
cmdTimerStatus = false;
bodyFrontLaserDistanceUpdate = true;
}
// move servo to some angle in position table
if(!cmdStepStatus) {
delay(5);
frontServo.write(readPosition[ 3 + (internalCmdStepCount * 3)]);
delay(50);
cmdStepStatus = true;
}
}
break;
}
} 
}

void cmdProcess() {
//
// run on all loops
//

if(!bodyMoveProcessStatus && !internalCmdProcessStatus) {
idleLoopCounter++;
} else {
idleLoopCounter = 0;
}

// we are idle for some time, lets run
if(idleLoopCounter > IDLE_LOOP_COUNTER_MAX) { 
// just go front
// TODO: a smarter behavior
prgCmd[0] = {0, commandGroupId, 2, 21, 2, 500, 0, 0, 0}; // HARDCODED commands
}

// process waiting received command
if(receivedCmd.cmd) {
switch(receivedCmd.cmd) {
case 11: //HARDCODED command
switch(prgCmd[prgIdx].extCmd) {
case 2: //HARDCODED command
case 3: //HARDCODED command
// log current command with current bodyMotorLeftStepsToGo
if(prgCmd[prgIdx].extCmd)
logCmd(prgCmd[prgIdx].extCmd, i2c_data[0][3].l);
// create command group
commandGroupId++;
// store addon program on prg temporary positions (last prgCmd positions)
prgCmd[PRG_CMD_MAX - TMP_PRG_OFFSET] = {0, commandGroupId, 11, 0, 1, 0, 0, 0, 0}; // HARDCODED commands
prgCmd[PRG_CMD_MAX - TMP_PRG_OFFSET + 1] = {0, commandGroupId, 0, 252, 0, 0, 0, 0, 0}; // HARDCODED commands
// store distance to go based on current bodyMotorLeftStepsToGo to use later
pausedUnits = stepsToUnits(i2c_data[0][3].l);
pausedCmd = cmd; 
// change the program index
prgIdxPaused = prgIdx;
prgIdx = PRG_CMD_MAX - TMP_PRG_OFFSET;
break;
}
break;

case 2: //HARDCODED command
switch(prgCmd[prgIdx].intCmd) {
case 252:
if(prgIdx >= PRG_CMD_MAX - TMP_PRG_OFFSET) {
// executing temporary program
// load the new commands
prgCmd[PRG_CMD_MAX - TMP_PRG_OFFSET] = {0, prgCmd[prgIdxPaused].group, 0, 0, prgCmd[prgIdxPaused].extCmd, pausedUnits, 0, 0, 0};
prgCmd[PRG_CMD_MAX - TMP_PRG_OFFSET + 1] = {0, prgCmd[prgIdxPaused].group, 0, 251, 0, prgIdxPaused + 1, 0, 0, 0}; // HARDCODED commands
// reset tmp storage variables
prgIdxPaused = 0;
pausedUnits = 0;
// change the program index
prgIdx = PRG_CMD_MAX - TMP_PRG_OFFSET;
} 
break;
}
break;

}
// reset control vars 
receivedCmd.cmd = 0;
// TODO: should only be set if the command its ok
bodyMoveProcessStatus = 0;
}

if(!bodyMoveProcessStatus || frontObstacleDetectedStatus == 1) { // TODO? ADD to if() internalCmdProcessStatus
//
// run on move first loop or on obstacle detected require stop
//

if(frontObstacleDetectedStatus == 1) { 
// log current command with current bodyMotorLeftStepsToGo
logCmd(prgCmd[prgIdx].extCmd, i2c_data[0][3].l);
// end sequence
resetPrgCmd();
// create command group
commandGroupId++; 
prgCmd[0] = {0, commandGroupId, 150, 0, 1, 0, 0, 0, 0}; // HARDCODED commands
prgCmd[1] = {0, commandGroupId, 0, 0, 2, -50, 0, 0, 0}; // HARDCODED commands
prgCmd[2] = {0, commandGroupId, 0, 150, 0, 11, 0, 0, 0}; // HARDCODED commands 11=frontServoPositions(1-11) 
prgIdx = 0;
// start stop command
frontObstacleDetectedStatus = 2;
}

// start a new move
cmdStart();
// get slave acknowledge and update slave data
getSlavesUpdate();
// detect commands received reports by slave
if(i2c_data[0][1].b[0] > 0) { // bodyMoveCmdRecId
// comand received
//Serial.print("DETECT REC"); Serial.println("\t");
slaveCmdReceivedId = i2c_data[0][1].b[0]; // TODO?
}
// detect command end reports by slave
detectSlaveCmdTerminated();

} else {
//
// run on all the others
//
// update slave data
getSlavesUpdate();
// detect command end reports by slave
detectSlaveCmdTerminated(); 
}

if(bodyMoveProcessStatus) {
odometry();
bodyMoveLoopCounter++; 
}

}

void detectSlaveCmdTerminated() {
// detect command end reports by slave
//Serial.print(i2c_data[0][0].b[0]); Serial.println("\t");
switch(i2c_data[0][0].b[0]) { // requestEventInfoType
case 1: // stop // is needed ????
case 2: // move, rotate
//Serial.print("DETECT END"); Serial.println("\t");
if(i2c_data[0][1].b[1] == slaveCmdWaitingId) { // bodyMoveCmdEndId
if(rotateScanStatus) rotateScanStatus = false;
if(frontObstacleDetectedStatus == 2) {
//Serial.print("DETECT END Obstacle 2"); Serial.print("\t");
frontObstacleDetectedStatus = 3;
}
slaveCmdTerminateId = slaveCmdWaitingId;
slaveCmdWaitingId = 0;
// TODO?: if is a move/rotate command
bodyMotorLeftStepsLast = 0; 
} 
break;
}
// process the slave terminated commands
// TODO: if(slaveCmdTerminateId > 0 || internalCmdTerminateId > 0) {
if(slaveCmdTerminateId > 0) {
//Serial.print("END slave"); Serial.print("\t");
//Serial.print(slaveCmdTerminateId); Serial.println("\t");
cmdEnd(); 
slaveCmdTerminateId = 0;
} 
}

void cmdStart() {
/*
Serial.print("START new command?"); Serial.print("\t");
Serial.print(prgIdx); Serial.print("\t"); 
Serial.println("\t");
*/

if(prgCmd[prgIdx].cmd > 0) {

// there is a external command?
if(prgCmd[prgIdx].extCmd > 0) {
//Serial.print("Primary slave"); Serial.println("\t");
slaveCmdRequestId++;
prgCmd[prgIdx].extId = slaveCmdRequestId;
sendSlaveMoveCmd(prgCmd[prgIdx].extId, prgCmd[prgIdx].extCmd, prgCmd[prgIdx].units, prgCmd[prgIdx].minSpeed, prgCmd[prgIdx].acceleration, prgCmd[prgIdx].maxSpeed);
bodyMoveProcessStatus = 1;
}

if(!internalCmdProcessStatus) {
//Serial.print("!internalCmdProcessStatus"); Serial.println("\t");
if(prgCmd[prgIdx].intCmd > 0) { 
//Serial.print("Primary internal"); Serial.println("\t"); 
// update command general control vars
cmd = prgCmd[prgIdx].cmd;
currentCommandGroupId = prgCmd[prgIdx].group;

// set specific command vars
switch(prgCmd[prgIdx].cmd) {
case 100: // scan rotate
rotateScanRequest = true;
rotateScanStatus = false; 
}

// reset some vars
internalCmdStepCount = 0;
internalCmdLoopCountNext = 0;
internalCmdProcessStatus = true;
}
}
} else {
// secondary group command
//Serial.print("New secundary command"); Serial.println("\t");

if(!internalCmdProcessStatus) {
//Serial.print("!internalCmdProcessStatus"); Serial.println("\t");

if(prgCmd[prgIdx].extCmd > 0) {
//Serial.print("Secondary slave"); Serial.println("\t");
slaveCmdRequestId++;
prgCmd[prgIdx].extId = slaveCmdRequestId;
sendSlaveMoveCmd(prgCmd[prgIdx].extId, prgCmd[prgIdx].extCmd, prgCmd[prgIdx].units, prgCmd[prgIdx].minSpeed, prgCmd[prgIdx].acceleration, prgCmd[prgIdx].maxSpeed);

// TODO?: where or bellow outside the if();
bodyMoveProcessStatus = 1;
}

if(prgCmd[prgIdx].intCmd > 0) {
//Serial.print("Secundary internal"); Serial.println("\t");
// set specific command vars
/* TODO: is required?
switch(prgCmd[prgIdx].cmd) {
case 100: // scan rotate
rotateScanRequest = true;
rotateScanStatus = false; 
}

*/
// reset some vars
// reset some vars
internalCmdStepCount = 0;
internalCmdLoopCountNext = 0;
internalCmdProcessStatus = true;
}
}
} 
}

void cmdEnd() {
boolean doPrgIdxUpdate = true;
// TODO: wait for last wheel to stop (to get current total pulses)

logCmd(prgCmd[prgIdx].extCmd, prgCmd[prgIdx].units);

// check what is the current internal command
switch(prgCmd[prgIdx].intCmd) {
case 251: // HARDCODED goto prgIdx command
/*
int f;
for(f=0; f<12; f++) {
Serial.print(prgCmd[f].cmd); Serial.print(" "); Serial.print(prgCmd[f].intCmd); Serial.print(" "); Serial.print(prgCmd[f].extCmd); Serial.print(" "); Serial.println(prgCmd[f].units);
}
*/
//Serial.print("GOTO "); Serial.println(prgCmd[prgIdx].units);
doPrgIdxUpdate = false;
// set next prgIdx
prgIdx = prgCmd[prgIdx].units;
// restore vars
currentCommandGroupId = prgCmd[prgIdx].group;
cmd = pausedCmd;
pausedCmd = 0;
// clean tmp prgCmd positions
resetTmpPrgCmd();
break;
case 252: // HARDCODED suspend program internal command
// pause advance in program index
delay(250);
// delay a bit and just keep execute pause
doPrgIdxUpdate = false;
//Serial.println("PAUSE");
break;
case 253: // HARDCODED restart program internal command
// restart command sequence (keep old commands)
doPrgIdxUpdate = false;
prgIdx = 0;
cmd = 0;
currentCommandGroupId = 0;
//Serial.println("RESTART");
break; 
}

if(doPrgIdxUpdate) {
// advance command sequence
prgIdx++;
if((prgIdx == PRG_CMD_MAX) || (!prgCmd[prgIdx].intCmd && !prgCmd[prgIdx].extCmd)) {
// reset command sequence (clean old commands)
resetPrgCmd();
//Serial.println("RESET");
}
}

// allow new command processing
bodyMoveProcessStatus = 0;
bodyMoveLoopCounter = 0;
internalMoveLoopCounter = 0;

internalCmdProcessStatus = false;
}

void logCmd(byte cmd, unsigned int units) {
// temporary log 
// TODO: save log somehere permanentely
// is stoped we have time to store more data in sdcard
if(cmd > 0) {
if(bodyMoveNextLog == BODY_MOVE_LOG_MAX) bodyMoveNextLog = 0;
bodyMoveLog[bodyMoveNextLog].cmd = cmd; 
// TODO: assure that we receive the real units, not the planed
bodyMoveLog[bodyMoveNextLog].units = units;
bodyMoveNextLog++;
}
}

void resetPrgCmd() {
int i;
// clean sequence
for(i=0; i < PRG_CMD_MAX; i++){
prgCmd[i].cmd = 0;
prgCmd[i].intCmd = 0;
prgCmd[i].extCmd = 0;
// TODO: should I clean all the itens?
}

// reset control vars
prgIdx = 0;
//internalCmdProcessStatus = 0;
cmd = 0;
currentCommandGroupId = 0;
}

void resetTmpPrgCmd() {
int i;
// clean sequence
for(i=PRG_CMD_MAX - TMP_PRG_OFFSET; i < PRG_CMD_MAX; i++){
prgCmd[i].cmd = 0;
prgCmd[i].intCmd = 0;
prgCmd[i].extCmd = 0;
//prgCmd[i].units = 0;
// TODO: should I clean all the itens?
}
}

void odometry() {
signed long distanceSteps;
float distanceUnits;
signed long leftStepsTarget;

boolean targetPositive = false;
boolean toGoPositive = false;

// if bodyMoveProcessStatus
if(i2c_data[0][0].b[1] == 1) {

leftStepsTarget = (signed long)i2c_data[0][2].l; // bodyMotorStepsTarget
bodyMotorLeftStepsToGo = (signed long)i2c_data[0][3].l; // bodyMotorLeftStepsToGo

/*
Serial.print(leftStepsTarget); Serial.print("\t");
Serial.print(bodyMotorLeftStepsToGo); Serial.print("\t");
*/

if(leftStepsTarget > 0) targetPositive = true;
if(bodyMotorLeftStepsToGo > 0) toGoPositive = true;

if(targetPositive) {
if(toGoPositive) {
bodyMotorLeftSteps = leftStepsTarget - bodyMotorLeftStepsToGo;
} else {
bodyMotorLeftSteps = leftStepsTarget - abs(bodyMotorLeftStepsToGo);
}
} else {
if(toGoPositive) {
bodyMotorLeftSteps = -(abs(leftStepsTarget) - bodyMotorLeftStepsToGo);
} else {
bodyMotorLeftSteps = -(abs(leftStepsTarget) - abs(bodyMotorLeftStepsToGo));
}
}

distanceSteps = bodyMotorLeftSteps - bodyMotorLeftStepsLast;
/*
Serial.print(bodyMotorLeftStepsLast); Serial.print("\t");
Serial.print(bodyMotorLeftSteps); Serial.print("\t");
Serial.print(distanceSteps); Serial.print("\t");
*/
bodyMotorLeftStepsLast = bodyMotorLeftSteps;

distanceUnits = stepsToUnits(distanceSteps);
//Serial.print(distanceUnits); Serial.print("\t");

switch(i2c_data[0][0].b[2]) { // bodyMoveCmd.cmd
case 2:
bodyPosX = bodyPosX + distanceUnits * cos(bodyThetaRadians);
bodyPosY = bodyPosY + distanceUnits * sin(bodyThetaRadians); 
break;
case 3:
bodyThetaRadians -= (float)(distanceUnits / (BODY_WHEEL_DISTANCE / 2));
if(bodyThetaRadians > PI2)
bodyThetaRadians -= PI2;
else if(bodyThetaRadians < -PI2)
bodyThetaRadians += PI2;
break;
}
/*
Serial.print(bodyThetaRadians * 180 / PI, 1); Serial.print("\t");
Serial.print(bodyPosX, 1); Serial.print("\t");
Serial.print(bodyPosY, 1); Serial.print("\t");
*/
//Serial.print(bodyThetaRadians); Serial.print("\t");
//Serial.println("\t");
}
}

/*
long unitsToSteps(float distance) {
return (signed long)(distance / bodyMotorStepsPerUnit);
}
*/
float stepsToUnits(signed long steps) {
return ((float)(steps * bodyMotorStepsPerUnit));
}
// EOF CINEMATIC CONTROL

//
//
// BOF SENSORS
//
//
void readSensors() {
// BOF: BATTERY VOLTAGE
batteryRead = analogRead(A3) * (BATERY_VOLTAGE / BATERY_MAX_OUT); //convert the value to a true voltage
batteryVoltage = batteryKalman.getFilteredValue(batteryRead); // kalman
// EOF: BATTERY VOLTAGE

// side distance update (one side per loop)
// data stored on bodySonarDistance[]
readSonarDistance();

// front distance update
// data stored on bodyFrontLaserDistance
readFrontLaserDistance();
}

// BOF: SparkFun VL53L1X 4M Laser Distance Sensor
int getFrontLaserDistance(byte mode) {
int r;
switch(mode) {
case 0: // HARDCODED
r = bodyFrontLaserRead;
break;
case 1: // HARDCODED
r = bodyFrontLaserDistanceQuick;
break;
case 2: // HARDCODED
default:
r = bodyFrontLaserDistance;
break; 
}
return r; 
}

void readFrontLaserDistance() {
// global: int bodyFrontLaserRead
// global: kalman bodyFrontLaserKalman
// global: int bodyFrontLaserDistance
// global: int bodyFrontLaserDistanceQuick
if(distanceSensor.newDataReady()) {
bodyFrontLaserRead = distanceSensor.getDistance();
// assure that return no more than max distance
if(bodyFrontLaserRead > FRONT_LASER_MAX_DISTANCE) {
bodyFrontLaserRead = 0;
bodyFrontLaserDistanceQuick = 0;
bodyFrontLaserDistance = 0;
} else {
// try to avoid front distance read polution
if(bodyFrontLaserDistanceUpdate) {
bodyFrontLaserDistance = bodyFrontLaserKalman.getFilteredValue(bodyFrontLaserRead); // kalman
bodyFrontLaserDistanceQuick = bodyFrontLaserDistance;
} else {
bodyFrontLaserDistanceQuick = bodyFrontLaserKalman.getFilteredValue(bodyFrontLaserRead); // kalman
}
/*
switch(prgCmd[prgIdx].intCmd) {
case 21: // HARDCODED
case 150: // HARDCODED
// front servo scan
// does not update all
if(bodyFrontLaserDistanceUpdate) {
bodyFrontLaserDistanceQuick = bodyFrontLaserKalman.getFilteredValue(bodyFrontLaserRead); // kalman
}
break;
default:
bodyFrontLaserDistance = bodyFrontLaserKalman.getFilteredValue(bodyFrontLaserRead); // kalman
bodyFrontLaserDistanceQuick = bodyFrontLaserDistance;
break;
}
*/
}
}
}
// EOF: SparkFun VL53L1X 4M Laser Distance Sensor

// BOF HC-SR04
void readSonarDistance() {
// global: kalman bodySonarKalman10, bodySonarKalman1
// global: int bodySonarRead[], bodySonarDistance[]
static byte bodySonarIndex = 0;
bodySonarRead[bodySonarIndex] = bodySonar[bodySonarIndex].ping_cm() * 10; // scaled to mm
//bodySonarRead[bodySonarIndex] = bodySonar[bodySonarIndex].ping_cm() * 10 + SIDE_SONAR_SIDE_OFFSET;
if(bodySonarRead[bodySonarIndex] == 0) {
bodySonarRead[bodySonarIndex] = SONAR_MAX_DISTANCE;
bodySonarDistance[bodySonarIndex] = 0;
}
switch(bodySonarIndex) {
case 0:
bodySonarDistance[bodySonarIndex] = bodySonarKalman0.getFilteredValue(bodySonarRead[bodySonarIndex]); // kalman 
break;
case 1:
bodySonarDistance[bodySonarIndex] = bodySonarKalman1.getFilteredValue(bodySonarRead[bodySonarIndex]); // kalman 
break;
}
bodySonarIndex++;
if(bodySonarIndex == SONAR_NUM) bodySonarIndex = 0; 
}
// EOF HC-SR04
// EOF SENSORS

//
//
// BOF: I2C arduino communication
// I2C limit write to 32 bytes
//
//
void getSlavesUpdate() {
// global union u_tag i2c_data[][];
byte i2c_byte[32];
int i;

// master reader (first loop data request)
for(i=0; i < 32; i++) {
i2c_byte[i] = 0;
}
i = 0;
Wire.requestFrom((int)i2cSlave[0], 32); // request 6 bytes from slave device #8
while (Wire.available()) { // slave may send less than requested
char c = Wire.read(); // receive a byte as character
i2c_byte[i] = c;
i++;
} 
for(i=0; i < 8; i++) {
i2c_data[0][i].b[0] = i2c_byte[i*4 + 0];
i2c_data[0][i].b[1] = i2c_byte[i*4 + 1];
i2c_data[0][i].b[2] = i2c_byte[i*4 + 2];
i2c_data[0][i].b[3] = i2c_byte[i*4 + 3];
}
}

void sendSlaveMoveCmd(byte id, byte cmd, int units, int minSpeed, int acceleration, int maxSpeed) {
// global: i2cSlave[0]
Wire.beginTransmission(i2cSlave[0]);
Wire.write(cmd); // cmd: rotate
Wire.write(id); // cmdId
Wire.write((byte*)&units, 2); // units, deg
Wire.write((byte*)&minSpeed, 2); // minSpeed
Wire.write((byte*)&acceleration, 2); // aceleration
Wire.write((byte*)&maxSpeed, 2); // maxSpeed
Wire.endTransmission();
slaveCmdWaitingId = id;
/*
Serial.print("sendSlaveMoveCmd"); Serial.print("\t");
Serial.print(cmd); Serial.print("\t"); 
Serial.print(id); Serial.print("\t"); 
Serial.print(units); Serial.print("\t");
Serial.print("-"); Serial.print("\t");
Serial.print(prgCmd[prgIdx].extId); Serial.print("\t"); 
Serial.print(prgCmd[prgIdx].group); Serial.print("\t"); 
Serial.print(prgCmd[prgIdx].cmd); Serial.print("\t"); 
Serial.print(prgCmd[prgIdx].extCmd); Serial.print("\t"); 
Serial.print(prgCmd[prgIdx].intCmd); Serial.println("\t"); 
*/
}
// EOF: I2C arduino communication

// BOF: nRF24L01
void sendRadioServoScanData(byte decision) {

rfSendData[0].b[0] = 1;
rfSendData[0].b[1] = decision;
rfSendData[0].b[2] = 0;
rfSendData[0].b[3] = 0;

rfSendData[1].i[0] = readPositionDistance[0];
rfSendData[1].i[1] = readPositionDistance[1];
rfSendData[2].i[0] = readPositionDistance[2];
rfSendData[2].i[1] = readPositionDistance[3];
rfSendData[3].i[0] = readPositionDistance[4];
rfSendData[3].i[1] = readPositionDistance[5];
rfSendData[4].i[0] = readPositionDistance[6];
rfSendData[4].i[1] = readPositionDistance[7];
rfSendData[5].i[0] = readPositionDistance[8];
rfSendData[5].i[1] = readPositionDistance[9];
rfSendData[6].i[0] = readPositionDistance[10];
rfSendData[6].i[1] = readPositionDistance[11];
rfSendData[7].i[0] = readPositionDistance[12];
rfSendData[7].i[1] = 0;

sendRadio(); 

}

void sendRadioLoopData() {
rfSendData[0].b[0] = 0;
rfSendData[0].b[1] = loopTime;
rfSendData[0].b[2] = prgIdx;
rfSendData[0].b[3] = cmd;

rfSendData[1].b[0] = prgCmd[prgIdx].intCmd;
rfSendData[1].b[1] = internalCmdProcessStatus;
rfSendData[1].b[2] = prgCmd[prgIdx].extCmd;
rfSendData[1].b[3] = bodyMoveProcessStatus;

rfSendData[2].i[0] = bodyFrontLaserRead;
rfSendData[2].i[1] = bodyFrontLaserDistance;

rfSendData[3].i[0] = bodySonarDistance[0];
rfSendData[3].i[1] = bodySonarDistance[1];

rfSendData[4].d = bodyThetaRadians * 180 / PI;
rfSendData[5].d = bodyPosX;
rfSendData[6].d = bodyPosY;
rfSendData[7].i[0] = (int)prgCmd[prgIdx].units;
rfSendData[7].i[1] = (int)stepsToUnits(bodyMotorLeftSteps);

sendRadio(); 
}

void sendRadio() {
bool rslt; 
rslt = radio.write(rfSendData, 32);
if (rslt) {
if ( radio.isAckPayloadAvailable() ) {
radio.read(&rfAckData, sizeof(rfAckData));
rfSendDataStatus = 2;
} else {
rfSendDataStatus = 1;
}
} else {
rfSendDataStatus = 1;
}
}
// EOF: nRF24L01

void distanceSensorsAdaptativePeriod( byte n ) {
int i;
for(i=0; i < n; i++) {
delay(30);
readFrontLaserDistance();
readSonarDistance();
/*
Serial.print(bodyFrontLaserDistance); Serial.print("\t"); 
Serial.print(bodySonarDistance[0]); Serial.print("\t"); 
Serial.print(bodySonarDistance[1]); Serial.println("\t"); 
*/
}
}

void sendSerialLoopData() {

int i;

Serial.print(loopTime); Serial.print("\t");
/*
Serial.print(rfSendDataStatus); Serial.print("\t");
Serial.print(rfAckData[0]); Serial.print("\t");
Serial.print(rfAckData[1]); Serial.print("\t");
Serial.print(rfAckData[2]); Serial.print("\t");
Serial.print(rfAckData[3]); Serial.print("\t");
*/
Serial.print(bodyFrontLaserRead); Serial.print("\t");
Serial.print(bodyFrontLaserDistance); Serial.print("\t");
//Serial.print(bodyFrontLaserDistanceQuick); Serial.print("\t");

for(i=0; i < SONAR_NUM; i++) {
//Serial.print(bodySonarRead[i]); Serial.print("\t");
Serial.print(bodySonarDistance[i]); Serial.print("\t");
}

Serial.print(bodyThetaRadians * 180 / PI, 1); Serial.print("\t");
Serial.print(bodyPosX); Serial.print("\t");
Serial.print(bodyPosY); Serial.print("\t");

Serial.print(prgIdx); Serial.print("\t"); 
Serial.print(cmd); Serial.print("\t"); 
Serial.print(prgCmd[prgIdx].intCmd); Serial.print("\t"); 
Serial.print(internalCmdProcessStatus); Serial.print("\t");
Serial.print(prgCmd[prgIdx].extCmd); Serial.print("\t"); 
Serial.print(bodyMoveProcessStatus); Serial.print("\t");
Serial.print(slaveCmdRequestId); Serial.print("\t");
Serial.print(slaveCmdReceivedId); Serial.print("\t");
Serial.print(slaveCmdWaitingId); Serial.print("\t");
Serial.print(slaveCmdTerminateId); Serial.print("\t");

Serial.print(bodyMoveLoopCounter); Serial.print("\t");
Serial.print(internalCmdLoopCountNext); Serial.print("\t");
Serial.print(internalCmdStepCount); Serial.print("\t");

//Serial.print(batteryRead); Serial.print("\t");
Serial.print(batteryVoltage); Serial.print("\t");

Serial.print(i2c_data[0][0].b[0]); Serial.print("\t"); // requestEventInfoType
Serial.print(i2c_data[0][0].b[1]); Serial.print("\t"); // bodyMoveProcessStatus
Serial.print(i2c_data[0][0].b[2]); Serial.print("\t"); // bodyMoveCmd.cmd
Serial.print(i2c_data[0][0].b[3]); Serial.print("\t"); // bodyMoveCmd.extId

Serial.print(i2c_data[0][1].b[0]); Serial.print("\t"); // bodyMoveCmdRecId
Serial.print(i2c_data[0][1].b[1]); Serial.print("\t"); // bodyMoveCmdEndId
//Serial.print(i2c_data[0][1].b[2]); Serial.print("\t"); // loopTime
Serial.print(i2c_data[0][1].b[3]); Serial.print("\t"); // loopTime

Serial.print((signed long)i2c_data[0][2].l); Serial.print("\t"); // bodyMotorStepsTarget
Serial.print((signed long)i2c_data[0][3].l); Serial.print("\t"); // bodyMotorLeftStepsToGo
Serial.print(i2c_data[0][4].d); Serial.print("\t"); // bodyMotorLeftSpeed
//Serial.print((signed long)i2c_data[0][5].l); Serial.print("\t"); // bodyMotorRightStepsToGo
//Serial.print(i2c_data[0][6].d); Serial.print("\t"); // bodyMotorRightSpeed
//Serial.print(i2c_data[0][7].l); Serial.print("\t"); // bodyMoveLoopCounter

Serial.println("\t");
}
/*
float getPointXpos(int d, float a) {
float b = bodyThetaRadians + (float)(a / 180.0) * PI;
return d * cos(b);
}
float getPointYpos(int d, float a) {
float b = bodyThetaRadians + (float)(a / 180.0) * PI;
return d * sin(b);
}
*/