6

I'm trying to make an RF remote control using ATtiny85 running at 8MHz on internal oscillator, this cheap RF 434MHz transmitter module (like the one below), and VirtualWire lib, but had no success so far.

RF transmitter

So, before I go any further, is it possible to use VirtualWire with ATtiny85 running at 3.6V and 8MHz with its internal oscillator? If so, how?

I've found a few scattered sources of information on the Net about it, but couldn't make much sense of it.

I have build ATMega328 versions of the receiver and transmitter using VirtualWire without any major problems. Both modules talk to each other will very little data drop out. Now, when I tried to replace the ATMega328 transmitter with a smaller version using the ATtiny85, I'm having problems.

Here are the symptoms:

  1. The major one seem to be that when I add the VirtualWire include, my sketch seems to slow down a lot. So I figure it's a timing issue. But I'm not sure what the problem is.

  2. I put a scope on ATtiny85 pin 6 (PB1) but I don't see anything being transmitted. I think I've messed up with the pin names.

  3. I can make the LED blink and also can read the buttons without problems.

Here are the schematics and the board design for the remote controller:

RC schematic

The board is currently powered by 2 AA NiMH batteries that are currently at 2.6V. I can make the ATtiny85 blink with that voltage. I plan to power the RC with a 3.6V CR2 lithum battery.

Here are more details of my development enviroment:

  • Arduino IDE 1.05
  • I'm using ATtiny cores from here
  • I'm programming the ATtiny85 using an Arduino UNO R3 as ISP, without problems (it's working as I can make the ATtiny85 blink an LED).

And here's my code:

#include <VirtualWire.h>

#define VCC_PIN  A1
#define BTN_PIN  A2
#define LED_PIN  A3
#define TXD_PIN  PB1

#define BLINK_UP_TIME  25

#define BUTTON_NONE  0
#define BUTTON_1     1
#define BUTTON_2     2
#define BUTTON_3     3
#define BUTTON_4     4
#define BUTTON_5     5
#define BUTTON_6     6

void setup()
{
  pinMode(BTN_PIN, INPUT);
  pinMode(TXD_PIN, OUTPUT);     
  pinMode(VCC_PIN, OUTPUT);     
  pinMode(LED_PIN, OUTPUT);     

  // leave RF transmitter off for now
  digitalWrite(VCC_PIN, LOW);

  // Initialise the IO and ISR
  vw_set_tx_pin(TXD_PIN);
  vw_setup(2000);    // Bits per sec

  // let the user know we are alive: heartbeat
  blink(3, 50);
  delay(1000);
}

uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;

void loop()
{
  byte button = ReadButtons();
  if (button != BUTTON_NONE) {
    // transmit button pressed
    digitalWrite(LED_PIN, HIGH); // Flash a light to show transmitting
    digitalWrite(VCC_PIN, HIGH); // turn tranmitter on
    delay(10);
    buf[0] = button;
    vw_send(buf, 1);
    vw_wait_tx(); // Wait until the whole message is gone
    digitalWrite(LED_PIN, LOW); // turn off LED
    digitalWrite(VCC_PIN, LOW); // turn tranmitter off
  }
  delay(100);
}

byte ReadButtons()
{
  unsigned int buttonVoltage = analogRead(BTN_PIN);
  byte button = BUTTON_NONE;   // return no button pressed if the below checks don't write to btn
  if      (buttonVoltage < 10)   {    button = BUTTON_1; } 
  else if (buttonVoltage < 200)  {    button = BUTTON_2; } 
  else if (buttonVoltage < 400)  {    button = BUTTON_3; } 
  else if (buttonVoltage < 550)  {    button = BUTTON_4; } 
  else if (buttonVoltage < 720)  {    button = BUTTON_5; } 
  else if (buttonVoltage < 900)  {    button = BUTTON_6; }
  else if (buttonVoltage > 1000) {    button = BUTTON_NONE; }
  return( button );
}

So, What's wrong with my setup?

Ricardo
  • 3,290
  • 2
  • 23
  • 52
  • You know you generally need to add an antenna on the transmitter module, right? Also, maybe try reducing the bits/second in the virtualwire setup too. – Connor Wolf Mar 18 '14 at 00:54
  • 1
    @FakeName well, to be honest, I didn't even solder the RF module to the board yet, as I can't even get the MCU to send the bits to the TX pin yet. So I'm putting the scope probe to the TX pin and if I see na attempt at a transmission, then I'll solder the module in. – Ricardo Mar 18 '14 at 01:02
  • Ah, whoops, missed that. You definitely need to look into proper pin-mapping. The virtualwire library does claim general AVR8 support, as well as having a specific note about the ATtiny84. I am curious, though. Some of your pins are `A{n}`, while the transmitter pin is `PB{n}`. Is that the correct pin mnemonic? – Connor Wolf Mar 18 '14 at 01:25
  • I don't see `PB1` defined in any of the ATtiny headers you link. Assuming that your transmitter is on PORT B, pin 1, I *think* you should have `#define TXD_PIN 1`, since port B is just accessed using the traditional digital IO mnemonic. – Connor Wolf Mar 18 '14 at 01:29
  • @FakeName After much debugging, the final `Ax` pin combination was the one that worked for me (except for the TXD pin, obviously). I guess you can refer to either `PBx` or `Ax` as they are macros that end up getting replaced by integers (guessing here). – Ricardo Mar 18 '14 at 01:33
  • 4
    The issue is that it looks like the static variables that `PBx` is referring to are *not* defined by the ATtiny libraries, so they may just kind of *incidentally* work. Have you verified you can controll PORT B 1 normally (e.g. with plain `digitalWrite()` commands? Alternatively, just swap the LED and TX pins, and use the LED pin (which you know works) for the data output. – Connor Wolf Mar 18 '14 at 01:55
  • 1
    Indeed, `PBn` is defined by AVR-Libc. It just so happens that arduino-tiny maps PBn to Dn, and each `PBn` is defined to have the value of `n` for use with `_BV()`. – Ignacio Vazquez-Abrams Mar 18 '14 at 02:40
  • You mention you use Arduino IDE, have you defined a new board for your ATtiny? This can be added to `ARDUINO_INSTALL_DIR/harware/arduino/boards`. Otherwise, you'd have to `#define VW_PLATFORM VW_PLATFORM_GENERIC_AVR` and use the additional calls needed for generic AVR platforms. – jfpoilpret Mar 18 '14 at 06:13
  • 1
    @jfpoilpret The attiny-master package installs a whole bunch of new ATtiny boards on the IDE. It gives support for several combinations of ATtiny45/84/85 with 1/8/16MHz with internal and external oscillators. I'm able to program the 85 with all 3 clocks when I comment out VW. – Ricardo Mar 18 '14 at 11:05
  • Do you have a way to check that the frequency of the MCU is the same as the frequency `F_CPU` used to compile your code? Typically, when a program seems to be slower than it should it is often due to a mismatch between `F_CPU` and the real frequency (which is setup by fuses in your case I suppose). – jfpoilpret Mar 18 '14 at 18:04
  • Does the slowdown occur when using 3.6V by comparison to using 5V? – jfpoilpret Mar 18 '14 at 18:05
  • The slowdown occur when I include the VirtualWire includes and function calls. If I comment them out, the timing gets back to normal. – Ricardo Mar 18 '14 at 18:19
  • Did you try just including VirtualWire but no function call? That would remove the possibility that the includes redefine some macros. – jfpoilpret Mar 18 '14 at 19:01
  • @jfpoilpret - I comment the include AND the calls in and out altogether, back and forth, when debugging. But the calls alone won't compile without the include. – Ricardo Mar 18 '14 at 19:09
  • 1
    My idea was actually to keep the include but remove the calls (which should compile), then check if you experiment a slowdown or not. If yes, that would means there's something in the include that overwrites one macro like `F_CPU`, or a static variable (class instance) that, when constructed, changes something important. – jfpoilpret Mar 18 '14 at 19:14
  • @jfpoilpret Got it. Will give it a try before I give up on the board. I'll probably take a step back and make an ATmega328 version of it. – Ricardo Mar 19 '14 at 11:25
  • I have had some clock problems with the ATtiny85 myself. I once tried to attatch an LCD to one and it would never work, only a few times i was able to make out mangled text. – TheDoctor Mar 19 '14 at 13:52

4 Answers4

3

Updated 26-08-2018: added tx(false); at the end of send. Without it, the TX pin could remain high, flooding the 433MHz band and making any other communication on it almost impossible!!!

Although these are all pretty old posts, I've still been struggling to get VirtualWire or its successor RadioHead working on an Attiny85. Typically the problem is that VirtualWire uses timer 0 and thereby breaks other stuff.

Now, for RX you typically don't want to spend all your processing cycles waiting for bits to come in, so you want it to be interrupt-driven, but for TX you can often do without that ... So, I did a bit of reverse-engineering of the VirtualWire TX code and implemented it as a simple send routine which can be used without needing a dedicated timer-interrupt. Using the code below on Attiny85 and an Arduino Uno as receiver with the normal VirtualWire library and the same baudrate, works just great for me, so if someone else can benefit from it, then here it is (including some code to read out an ultrasonic distance sensor and driving some leds - up to the reader to strip out what he doesn't need):

#include <util/crc16.h>

#define LED_PIN           3
#define LED2_PIN          4

#define TX_PIN            2
#define TX_BAUDRATE       2000
#define TX_MICROINTERVAL  (1000000UL/TX_BAUDRATE)
#define TX_MAXLEN         12

#define TRIGGER_PIN       0
#define ECHO_PIN          1

inline void led(const bool onOff) { digitalWrite(LED_PIN, onOff ? HIGH : LOW); }
inline void led2(const bool onOff) { digitalWrite(LED2_PIN, onOff ? HIGH : LOW); }
inline void tx(const bool onOff) { digitalWrite(TX_PIN, onOff ? HIGH : LOW); }
inline void trigger(const bool onOff) { digitalWrite(TRIGGER_PIN, onOff ? HIGH : LOW); }

const uint8_t header[8] = { 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x38, 0x2C };
const uint8_t conv4to6[16] = { 0x0D, 0x0E, 0x13, 0x15, 0x16, 0x19, 0x1A, 0x1C,
                               0x23, 0x25, 0x26, 0x29, 0x2A, 0x2C, 0x32, 0x34 };

void rawSend(const uint8_t * p, uint8_t len) {
  while (len--) {
    const uint8_t val = *p++;
    for (uint8_t mask = 1; mask != 0x40; mask <<= 1) {
      tx(val & mask);
      delayMicroseconds(TX_MICROINTERVAL);
    }
  }
}

void send(const uint8_t * buf, const uint8_t len) {
  //  First create the payload ...
  static uint8_t payload[(TX_MAXLEN+3)*2];
  uint8_t * p = payload;
  uint16_t crc = 0xFFFF;
  uint8_t v = len + 3;
  crc = _crc_ccitt_update(crc, v);
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];
  for (uint8_t l = len; l--;) {
    v = *buf++;
    crc = _crc_ccitt_update(crc, v);
    *p++ = conv4to6[v >> 4];
    *p++ = conv4to6[v & 0x0F];
  }
  crc = ~crc;
  v = (uint8_t)crc;
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];
  v = (uint8_t)(crc >> 8);
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];

  //  Now transmit the header and the payload ...
  rawSend(header, sizeof(header));
  rawSend(payload, (len + 3)*2);
  tx(false);
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  led(false);
  led2(false);

  pinMode(TX_PIN, OUTPUT);
  tx(false);

  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  trigger(false);
}

int measure() {
  trigger(false); delay(10); trigger(true); delay(10); trigger(false);

  //  We support up to 6m, i.e. 6000mm, so that would take
  //  6000mm * 2 / 0.34mm/ms = 35294ms to get to the end
  //  and back, hence let's use a timeout along those
  //  lines ...
  unsigned long duration = pulseIn(ECHO_PIN, HIGH, 36000);
  if (!duration || (duration > 35500)) return 0;
  return (int)(duration * 34 / 200);
}

int bufferedMeasure() {
  static int lastVal = 0;
  static bool toggle = true;
  int newVal = measure();
  if (newVal) {
    led2(toggle);
    toggle = !toggle;
    lastVal += (newVal-lastVal)/10;
  }
  return lastVal;
}

// the loop routine runs over and over again forever:
void loop() {
    //  Measure the distance ...
    int val = bufferedMeasure();
    if (val) {
      static unsigned long data;
      led(true);
      data = 0xD1570000UL|val;
      send((const uint8_t *)&data, sizeof(data));
      led(false);
    } else {
      led(true); delay(100); led(false); delay(100);
      led(true); delay(100); led(false); delay(100);
      led(true); delay(100); led(false);
    }
    delay(1000);
}
Mr.K
  • 31
  • 2
3

I tried those Tiny cores you are using and had a lot of problems. I switched to http://code.google.com/p/arduino-tiny/ and it seems much better. Create a second Arduino installation folder and add the arduino-tiny cores. The pin name constants are like PIN_A0, PIN_A1,...,PIN_B1, etc. So, try that out.

I looked briefly at the VirtualWire code and there are ifdefs specifically for the Attiny85, so it should work. Here is a derivative project with working ATTINY85 examples: http://cosa-arduino.blogspot.de/2013/03/news-virtual-wire-interface.html

imjosh
  • 518
  • 2
  • 5
1

Try the manchester library instead. This post includes all the necessary info except wiring diagrams: Manchester Library Won't Compile for Attiny85

Joel
  • 523
  • 3
  • 15
1

I spent some time getting VirtualWire to work on attiny84. I see two issues with your code:

  1. Since the tiny85 only has one timer, that one is being used by both VirtualWire and millis/micros. So your delays won't work as expected.
  2. VirtualWire uses default values for vw_ptt_pin (10) and vw_rx_pin (11). Those pins don't exist on the attiny85. When vwsetup calls pinMode with these values, it causes writes to arbitrary memory addresses. This is likely to make your sketch behave unpredictably. To fix set vw_set_ptt_pin() and vw_set_rx_pin() to pins that exist on the tiny85 or modify the VirtualWire source.
Ricardo
  • 3,290
  • 2
  • 23
  • 52
user2973
  • 1,361
  • 8
  • 12
  • Thanks for your answer. I had notice symptoms of the item 1 above: the timing got all wrong when I tried to make VirtualWire work. Item 2 seems to be good reasons for the whole thing not to work. My workaround for this problem was to stick with ATmega328 for RF stuff. I'll take a look and see if I can get back to using VW with my ATTinies. – Ricardo Jul 21 '14 at 01:07