Serial print function printing zeros when used with high frequency

Hi guys!

I have a small problem with my project. It is a pololu zumo robot with Arduino Uno, HC-06 bluetooth module, servo and HC-SR04 sensor. The pololu board has LSM303D accelerometer and L3GD20H gyroscope.

I calculate gyro’s z and accelerometer’s x & y changes and try to send them over Serial port with 100Hz frequency (Timer0). When I try to print only gyro data, everything works fine, the angle value changes properly when rotating the robot. But when adding more data to printing, the Serial prints only zeros.

Are there any Serial frequency limits that could cause this situation? Unfortunately, I need Serial to communicate with HC-06 as using any other library conflicts with the servo (which is using Timer2).

#include <Wire.h>
#include <ZumoShield.h>
#include <LSM303.h>

#define L3G_CTRL1 0x20
#define L3G_CTRL2 0x21
#define L3G_CTRL3 0x22
#define L3G_CTRL4 0x23

#define LSM303_CTRL_1 0x20
#define LSM303_CTRL_2 0x21

#define SAMPLE_FREQUENCY 100.16025641025641 //Hz
#define ACC_SCALE 2
int L3G_I2C_ADDRESS = 107;
int LSM303_I2C_ADDRESS = 29;

double gz;
double gzSum;
double gzInit = 0.0;
double zAngle;

double ax;
double axSum;
double ay;
double aySum;

int it;

ZumoMotors motors;
Pushbutton button(ZUMO_BUTTON);

void setupTimer0();
void setupL3GD20(int scale);
void setupLSM303D();
void getSensorRawValues();
void calculateSensorOutput();
void calibrateGyro(int nrOfSamples);

void setup()
{
    Wire.begin();
    Serial.begin(9600);
    setupL3GD20(250);
    setupLSM303D();
    calibrateGyro(1000);
    zAngle = 0.0;
    gzSum = 0.0;
    axSum = 0.0;
    aySum = 0.0;
    it = 0;
    button.waitForButton();
    Serial.println("SETUP COMPLETE");
    setupTimer0();
}

void loop()
{
    getSensorRawValues();
}

ISR(TIMER0_COMPA_vect) {
    calculateSensorOutput();
}

void setupTimer0() {
    noInterrupts();
    // Clear registers
    TCCR0A = 0;
    TCCR0B = 0;
    TCNT0 = 0;

    // 100.16025641025641 Hz (16000000/((155+1)*1024))
    OCR0A = 155;
    // CTC
    TCCR0A |= (1 << WGM01);
    // Prescaler 1024
    TCCR0B |= (1 << CS02) | (1 << CS00);
    // Output Compare Match A Interrupt Enable
    TIMSK0 |= (1 << OCIE0A);
    interrupts();
}

int readRegister(int deviceAddress, byte registerAddress){
    int v;
    Wire.beginTransmission(deviceAddress);
    Wire.write(registerAddress);
    Wire.endTransmission();
    Wire.requestFrom(deviceAddress, 1);
    v = Wire.read();
    Wire.endTransmission();
    return v;
}

void writeRegister(int deviceAddress, byte address, byte val) {
    Wire.beginTransmission(deviceAddress); // start transmission to device
    Wire.write(address);       // send register address
    Wire.write(val);         // send value to write
    Wire.endTransmission();     // end transmission
}

void setupL3GD20(int scale){
    // Enable x, y, z, turn off power down, ODR 100 Hz.
    writeRegister(L3G_I2C_ADDRESS, L3G_CTRL1, 0b00001100);

    // If you'd like to adjust/use the HPF, you can edit the line below to configure CTRL_REG2:
    writeRegister(L3G_I2C_ADDRESS, L3G_CTRL2, 0b00000000);

    // Configure CTRL_REG3 to generate data ready interrupt on INT2
    // No interrupts used on INT1, if you'd like to configure INT1
    // or INT2 otherwise, consult the datasheet:
    writeRegister(L3G_I2C_ADDRESS, L3G_CTRL3, 0b00001000);

    // Measurement range [dsp]
    if(scale == 250) {
        writeRegister(L3G_I2C_ADDRESS, L3G_CTRL4, 0b00000000);
    } else if(scale == 500) {
        writeRegister(L3G_I2C_ADDRESS, L3G_CTRL4, 0b00010000);
    } else {
        writeRegister(L3G_I2C_ADDRESS, L3G_CTRL4, 0b00110000);
    }
}

void setupLSM303D() {
    // Normal power mode, all axes enabled
    writeRegister(LSM303_I2C_ADDRESS, LSM303_CTRL_1, 0b00100011);

    // Acceleration full-scale 2g
    writeRegister(LSM303_I2C_ADDRESS, LSM303_CTRL_2, 0b00000000);
}

void getSensorRawValues() {
    byte OUT_Z_L = readRegister(L3G_I2C_ADDRESS, 0x2C);
    byte OUT_Z_H = readRegister(L3G_I2C_ADDRESS, 0x2D);
    gz = (((OUT_Z_H << 8) | OUT_Z_L) * 0.00875) - gzInit;
    gzSum += gz;

    byte OUT_X_L = readRegister(LSM303_I2C_ADDRESS, 0x28);
    byte OUT_X_H = readRegister(LSM303_I2C_ADDRESS, 0x29);
    byte OUT_Y_L = readRegister(LSM303_I2C_ADDRESS, 0x2A);
    byte OUT_Y_H = readRegister(LSM303_I2C_ADDRESS, 0x2B);
    int16_t X = ((OUT_X_H << 8) | OUT_X_L);
    int16_t Y = ((OUT_Y_H << 8) | OUT_Y_L);
    ax = ((double) X / 0x8000 * ACC_SCALE);
    ay = ((double) Y / 0x8000 * ACC_SCALE);
    axSum += ax;
    aySum += ay;

    it++;
}

void getCalibrationValues() {
    byte OUT_Z_L = readRegister(L3G_I2C_ADDRESS, 0x2C);
    byte OUT_Z_H = readRegister(L3G_I2C_ADDRESS, 0x2D);
    gz = (((OUT_Z_H << 8) | OUT_Z_L) * 0.00875);
}

void calculateSensorOutput() {
    double gz = (it == 0) ? 0 : gzSum / it;
    double zAngleChange = gz * (1.0 / SAMPLE_FREQUENCY);
    if (!(zAngleChange < 0.01 && zAngleChange > -0.01 )) {
        zAngle += zAngleChange;
        //zAngle2 -= zAngleChange;
    }
    Serial.print(zAngle);
    Serial.print(" ");
    gzSum = 0.0;

    double ax = (it == 0) ? 0 : axSum / it;
    double ay = (it == 0) ? 0 : aySum / it;
    Serial.print(ax);
    Serial.print(" ");
    Serial.println(ay);
    axSum = 0;
    aySum = 0;

    it = 0;
}

void calibrateGyro(int nrOfSamples) {
    for (int i=0;i<nrOfSamples;i++) {
        getCalibrationValues();
        gzInit += gz;
    }
    gzInit /= nrOfSamples;
    Serial.print("gzInit: ");
    Serial.println(gzInit);
}

The first rule of Arduino Club is you do not do serial I/O in interrupt context.

Hi,

Thanks for your quick answer. Could you explain why? Are the Serial I/O and interrupts somehow dependent?

If I cannot use the Serial I/O what would you suggest for communication with the HC-06? All SoftwareSerial type libraries are excluded due to interference with the Servo. Or maybe using the interrupts is wrong? Maybe I should use millis() instead? Surely it will not be as precise.

Thanks in advance.

Serial I/O is buffered, so when you for a Serial.print or write, the write function checks to see if there is space in a 64 byte software-xontrolled buffer.
If there is, it writes the byte to the buffer and returns.
If there is no space, the write function waits until there is.

Meanwhile, the processor is interrupted whenever the USART transmit register is empty and ready to accept another character.
If you’re in interrupt context, interrupts are disabled, so if the 64 byte buffer is full, the tx register empty interrupt can never be called, so the write function spins uselessly waiting for space in the buffer.

9600-baud is (at best?) 960 characters per second. And you are sending messages 100 times a second. I hope those messages average less than 9.6 characters!