2

TL;DR:

  • Arduinos are current hungry so I want to switch my microcontroller
  • code works on Uno, I change upload to ATtiny85, code doesn't compile
  • Wire.h breaks at the TWBR input
  • Should I keep trying to fix this or should I just use ATmega328P without the Arduino?



So I'm working on a project where I send data from an IMU (MPU6050) to a phone using Bluetooth LE (HC08) with a bit of signal filtering by a microcontroller in between. I've been using Pro Minis but the amperage consumption by these boards is ridiculous, so I'm trying to replace these with some ATtiny85's that I have laying around.

The problem is, the Wire.h library won't compile for the ATtiny85. It's also worth noting that the default MPU6050 code mandates use of an interrupt pin, which ATtiny's don't have, so I've modified the code to work without it (I'm not sure if that's part of the problem).

#include <SoftwareSerial.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"

//==============================================================================
// MPU6050 initialization code
MPU6050 mpu;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[64];
Quaternion q;
VectorInt16 aa;
VectorInt16 aaReal;
VectorInt16 aaWorld;
VectorFloat gravity;

//==============================================================================
// microcontroller initialization code
int avg_counter = 0;
int pts_to_avg = 15;
int * x_array = (int*)malloc(sizeof(int) * 25);
int * y_array = (int*)malloc(sizeof(int) * 25);
int * z_array = (int*)malloc(sizeof(int) * 25);

//==============================================================================
// HC-08 initialization code
const byte rxPin = 3;
const byte txPin = 4;
SoftwareSerial bleSerial(rxPin, txPin);

//==============================================================================
void setup() {
  // MPU6050 setup code ========================================================
  Wire.begin();
  TWBR = 24;
  mpu.initialize();
  mpu.dmpInitialize();
  mpu.setXAccelOffset(-1343);
  mpu.setYAccelOffset(-1155);
  mpu.setZAccelOffset(1033);
  mpu.setXGyroOffset(19);
  mpu.setYGyroOffset(-27);
  mpu.setZGyroOffset(16);
  mpu.setDMPEnabled(true);
  packetSize = mpu.dmpGetFIFOPacketSize();
  fifoCount = mpu.getFIFOCount();

  // microcontroller setup code ================================================


  // HC-08 setup code ==========================================================
  // make sure your HC-08 is set to the same baud as below!
  bleSerial.begin(19200);
  delay(100);
}

//==============================================================================
void loop() {
  while(fifoCount < packetSize){
    fifoCount = mpu.getFIFOCount();
  }

  if(fifoCount == 1024){
    mpu.resetFIFO();
    bleSerial.println(F("FIFO overflow!"));
  }else{
    if(fifoCount % packetSize != 0){
      mpu.resetFIFO();
    }else{
      while(fifoCount >= packetSize){
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        fifoCount -= packetSize;
      }

      mpu.dmpGetQuaternion(&q,fifoBuffer);
      mpu.dmpGetAccel(&aa, fifoBuffer);
      mpu.dmpGetGravity(&gravity, &q);
      mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
      mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);

      // points are averaged to keep the baud low w/o FIFO overflow
      if(avg_counter <= pts_to_avg){
        // add live data to this array and hold onto it to be averaged
        x_array[avg_counter] = aaWorld.x;
        y_array[avg_counter] = aaWorld.y;
        z_array[avg_counter] = aaWorld.z;
        avg_counter++;
      }else{
        // average data from the array
        int x_average=0;
        int y_average=0;
        int z_average=0;

        // sums the data in #_average
        for(int i = 0; i < avg_counter; i++){
          x_average += x_array[i];
          y_average += y_array[i];
          z_average += z_array[i];
        }

        // divides by avg_counter (which should == pts_to_avg)
        x_average /= avg_counter;
        y_average /= avg_counter;
        z_average /= avg_counter;

        // resets counter so the array data can be overwritten with future data
        avg_counter = 0;

        // prints data to serial port (if BT is connected, sends via BT)
        bleSerial.print(x_average);
        bleSerial.print(",");
        bleSerial.print(y_average);
        bleSerial.print(",");
        bleSerial.println(z_average);
      }
    }
  }
}

When using Arduino as ISP, selecting "Board: ATtiny25/45/85", "Processor: ATtiny85", & "Clock: Internal 1MHz", the following error showed up:

exit status 1
'TWBR' was not declared in this scope

this error did not occur when compiling for the Uno. If you comment out TWBR, then you get an enormous error message (I'll only paste a bit):

In file included from c:\users\auphynne\appdata\local\arduino15\packages\arduino\tools\avr-gcc\4.9.2-atmel3.5.4-arduino2\avr\include\avr\io.h:99:0,

                 from C:\Users\auPhynne\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.21\libraries\Wire\src\utility\twi.c:25:

C:\Users\auPhynne\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.21\libraries\Wire\src\utility\twi.c:426:15: error: 'TWINT' undeclared (first use in this function)

    TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;

               ^

C:\Users\auPhynne\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.21\libraries\Wire\src\utility\twi.c:426:28: error: 'TWSTA' undeclared (first use in this function)

    TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;

                            ^

C:\Users\auPhynne\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.21\libraries\Wire\src\utility\twi.c:426:40: error: 'TWEN' undeclared (first use in this function)

    TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;

                                        ^

exit status 1
Error compiling for board ATtiny25/45/85.

To try to remedy this, I went into

C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src\utility\twi.c

and hardcoded this line from

// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;

to

// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = 24;

and I still got the same gruesome error message.

Pins are what you'd expect, 5v power to both modules and grounded, plus:

  • Uno D4 -> HC08 RXD
  • Uno D3 -> HC08 TXD
  • Uno SCL -> MPU6050 SCL
  • Uno SDA -> MPU6050 SDA

(again note that the interrupt pin is not used, and this worked quite well when building on the Uno! I got data on my phone's BT serial terminal app so that doesn't seem to be the problem)

Any ideas on how I can get Wire.h to compile on my ATtiny85?

Or... should I just abort and go with the ATmega328P (without the accompanying Uno) since it comes in the Quad Flat SMD package? (footprint matters for the long term)

  • 3
    If you really want to minimize power consumption, you should probably connect the sensor directly to custom firmware on a BTLE MCU, without an intervening part. You can run Arduino code on an nRF51822, though that may not be the lowest power software approach for it. Also a MEMS sensor + BTLE is a common demo for the companies which make the latter, so you may be able to find the hardware already made with working example software. – Chris Stratton Feb 27 '18 at 19:04
  • 1
    I'd suggest going with an Arduino Pro Mini, (and an external usb-to-serial converter). Those use the same ATMega328P, but without the power wasting extra's of the uno. They are also a lot smaller. You could do some modifications to the board to get a bit more battery life, like removing the power led. – Gerben Feb 27 '18 at 19:33
5

You get this error, because the ATTiny85 doesn't have full I2C hardware. It only as an USI (Universal Serial Interface). This can be utilized to do the rest of the I2C protocol via software. There are some libraries out there to do that, most of them are somewhat named TinyWire. There were dedicated libraries for master and slave operation named TinyWireM and TinyWireS. And I composed one library with both functionalities (and some more) in TinyWire. The last one - I think - is not usable for you, because theSoftwareSerial library needs the PinChangeInterrupt to work, but TinyWire uses it for detecting stop conditions on the I2C bus.

So for your case you can use the TinyWireM library. Note that the master operations need exact timing. Using to much interrupts in this time can cause problems in transmission. Test it and disable interrupts, if it is needed.

  • 1
    I'm using the MPU6050 in my project and the library MPU6050.h, which is includes library I2Cdev.h, which includes library Wire.h and uses it a lot - I have a sinking feeling that it will not suffice to replace all instances of Wire.foo() with TinyWireM.foo() in the I2Cdev.h library.. ? The library TinyWireM.h absolutely looks like the right solution, thank you so much, but if implementing it is going to be too complex I'm not sure I'd have the coding skill to pull it off (in which case I'll just stick with ATmegas :l ) – Austin Fennacy Feb 27 '18 at 20:31
  • 1
    In principle the TinyWireM library has many of the same functions as the Wire library (except for the slave functions). But there might be other issues, with simply changing this. You can try to change it, the easier way would be to use an Arduino Pro mini (as mentioned by Gerben). And if this answered your question, please mark it as correct :D – chrisl Feb 28 '18 at 10:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.