RS485 with attiny45/85 [SOLVED]

Hello, everyone. First of all, I'd like to point out that I did my research and I can't find relevant info, therefore I ask.

I'd like to create a simple project with arduino communicating over 50-70meters, sending or receiving simple 8bit values. It should operate remote relays, read counters, etc. From what I found, I assume, the best way for such long-range communication is RS485 protocol (I have some experience with i2c, but it's not capable of such long-range transmits). I'd like to use chip similar to MAX485. I found tons of examples on how to work with Arduino and Max485. However, as all of the slave devices in my case will only need 1-bit output/input, I'd like to utilize ATtiny, either 45 or 85 (depending on the final code size). It would also be lovely if I can utilize the internal 8MHz oscilator on the tiny, limiting the number of parts for each slave. However, I am unable to find any examples of such design (only ATtiny2313). Is it even possible to utilize tiny45 for such communication? I'd like to point out that I do not care about the communication speed - I need only to transmit few bytes per second (like 10-12 bytes, nothing more). Thanks in advance for any guidance/advices.

http://www.gammon.com.au/forum/?id=11428

Is it even possible to utilize tiny45 for such communication?

I don't see why not. RS485 is an electrical protocol. It doesn't care what device is at the end of it. If you can send async serial from a Tiny45 you should be able to use it.

Thanks for the hint. I am more-or-less looking for someone with practical experience with such setup :)

Dear sancho_sk,

RS485 is perfect for your project. You will have to formulate a protocol with request/response format and that implements frame checking (error checking) on the data sent, especially if you are switching relays on/off remotely.

Nick Gammon's library seems to implement all of this and will definitely cut down on design and testing..

Be aware that when using the internal oscillator and you are using the UART functionality that temperature deviations will alter the clock frequency that might offset the bit timing making it very difficult to synchronize the start of a byte.

Modbus will also be a very good option but might be to large for a tiny.

Thanks, I'll check the library. I already ordered the devices. How serious can the temrature impact be if the communication speed will be set to ~1200bps?

Async serial will tolerate a clock error of around 9%.

Between bytes it resyncs (that is what the start bit is about) so a slight pause between sending bytes (if needed by experimentation) would allow it to resync each time.

In order to make the RS485 transmissions as painless as possible, I highly recommend Bill Porters EasyTransfer library.

Thanks for the EasyTransfer hint, but it seems more appropriate for communication of 2 arduinos, where as I have multiple slave devices.

All depends on your application. However, if the aim is to reliably and easily transfer data serially (or via rs485) then easy transfer is a huge time saver.

RS485 and EasyTransfer is the way to go. Slave address: You can set the address of each slave device with dip switches, pins and shunts, or you can set it in software. Each way has it's pros and cons. Easytransfer uses a data structure to send information. Add a variable to the data structure for the "address" of each device. The master will send out a packet to the desired slave but setting the "address" in the data structure. Each slave will receive the packet and check to see if the "address" matches. If it does, the slave device acts on the data that it received. The other slaves will ignore it and go back to whatever it was they were doing.

I use the MAX1487 http://pdf1.alldatasheet.com/datasheet-pdf/view/73460/MAXIM/MAX1487.html. It has a max data rate of 2.5Mbps and you can have 128 devices on one twisted pair(TP). It also costs about the same as the MAX485.http://www.aliexpress.com/item/Free-Shipping-MAX1487-MAX1487EPA-DIP-8-Made-In-China-Series-100-New-and-High-Quality/1061742692.html.

If you have any Cat5 cable around, you can use one TP for data and then run power over the remaining 6 wires. It's small wire so you can't run much power down it without a voltage drop. Cat5 cable is cheap and so are the connectors. Makes for a clean setup.

attiny44,84,85, 2313?: The comparison chart http://en.wikipedia.org/wiki/Atmel_AVR_ATtiny_comparison_chart shows that only the 2313 has a hardware UART. The 84 and 85 do have a USI, but EasyTransfer uses the UART. BUT...the EasyTransfer library now has a software EasyTransfer, so you can use the 44, 84 or 85 if you have enough program space. That being said, I would go with the 44; I can get them cheaper than I can get the 85, has more pins etc. http://www.ebay.com/itm/10x-Atmel-8-bit-Microcontroller-ATTINY44A-PU-10pcs-/330942429719?pt=LH_DefaultDomain_2&hash=item4d0db4ba17

fairwindsii: Each slave will receive the packet and check to see if the "address" matches. If it does, the slave device acts on the data that it received. The other slaves will ignore it and go back to whatever it was they were doing.

Thanks for this idea - it's quite easy and straight forward, so I'll give it a shot for sure.

fairwindsii: I use the MAX1487 http://pdf1.alldatasheet.com/datasheet-pdf/view/73460/MAXIM/MAX1487.html. It has a max data rate of 2.5Mbps and you can have 128 devices on one twisted pair(TP).

Sorry for the amateur question, but I failed to see the advantages of 1487 against the 485. Can you, please, elaborate? Maybe I missed something important :(

fairwindsii: If you have any Cat5 cable around, you can use one TP for data and then run power over the remaining 6 wires.

Exactly my idea. I know the recommendations for power transfer over Cat-5 cable, it's way more than I really need for my project.

fairwindsii: attiny44,84,85, 2313?: The comparison chart shows that only the 2313 has a hardware UART. The 84 and 85 do have a USI, but EasyTransfer uses the UART. BUT...the EasyTransfer library now has a software EasyTransfer, so you can use the 44, 84 or 85 if you have enough program space.

As the only purpose of these chips will be to receive data and set one output according the data, or, in special cases, act as a counter using HW interrupt and on request send the number of counts as an answer, I assume there will be a lot of space left on all of the chips. The number of pins is not a limitation, quite the opposite (I can make the design smaller), again considering the usage, so I am just fine with 8-DIP package (my only concern was the UART, but it seems to be handled by the EasyTransfer, as you pointed out). I don't want to make free advertising, but the shop I am using has a better price for ATtiny85 and are quite fast as of delivery (http://www.tme.eu/sk/details/attiny85-20pu/mikroprocesory-atmel-avr-tht/atmel/#). However, I have to thank all of the people here - within 2 days I have been guided and provided with information I would have to gather manually for weeks using trial-and-error approach.

I don't want to make free advertising, but the shop I am using has a better price for ATtiny85 and are quite fast as of delivery (http://www.tme.eu/sk/details/attiny85-20pu/mikroprocesory-atmel-avr-tht/atmel/#).

Wow on the price! I'll have to remember that source for the attiny chips. Digikey comes close but you have to buy 25+. It may be free advertising, but if some one has a better cheaper source, let everyone know. :D

Sorry for the amateur question, but I failed to see the advantages of 1487 against the 485. Can you, please, elaborate? Maybe I missed something important smiley-sad

MAX485 vs MAX1487: My only point on that was simple. If I'm buying components I try to buy more than I need for a project, because I almost always will find another application for them. If the price differential is small between a lower spec'd device and a higher spec'd device, I'll go with the higher spec'd device. The next project may need the higher available speed. That's all I was saying. If you already have the other 485 chips, there's no need to order something else. :)

Device data rate device on line current MAX485 0.25Mbps 32 300ma MAX1487 2.50Mbps 128 230ma

The forum is great. Lots of people, with lots of experience, and lots of ideas.

Data rate device on line current
MAX485 0.25Mbps 32 300ma
MAX1487 2.50Mbps 128 230ma

My plan is to use SN75176, which has slightly more idle current, but, on the other hand can deliver 5Mbits and (more important from my perspective) costs peanuts at the same site I buy ATtny.
But the number of devices for MAX1487 looks interesting.

sancho_sk,

Just in case you're not familiar with structures this is the code for a simple structure:

struct DATA_STRUCTURE{
  //put your variable definitions here for the data you want to receive
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  unsigned address;
  int blinks;
  int pause;
  int potValue;
  unsigned long  time;
  int var1;
  int var2;  
};
DATA_STRUCTURE mydata;  

if(mydata.address == myaddress){
  //then do this with the data
  }

You can access the values in the structure by using nameofstucture.elementname.

i.e. scaledPotValue = (mydata.potValue)/scalingFactor;

Thanks.
Seems reasonably easy and straightforward.
Today the pards ordered on Saturday should arrive, I’ll update the thread as soon as I test both the ATtiny communication and the library.

I have to report unfortunate failure.
I did a quick wiring on prototyping board, very similar to the described guidance on the Nick Gammon’s site.
I set the speed to 9600 and 4800, none of which worked.
Unfortunately, I do not have 2 arduinos, so I programmed one Tiny85 with a code like this:

#include "RS485_protocol.h"
#include <SoftwareSerial.h>


const byte ENABLE_PIN = 3;
const byte LED_PIN = 4;

SoftwareSerial rs485 (0, 1);  // receive pin, transmit pin

// callback routines
  
void fWrite (const byte what)
  {
  rs485.print (what);  
  }
  
int fAvailable ()
  {
  return rs485.available ();  
  }

int fRead ()
  {
  return rs485.read ();  
  }

void setup()
{
  pinMode(0,INPUT);
  pinMode(1,OUTPUT);
  rs485.begin (4800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // LED
}  // end of setup


void loop()
{  
  byte buf [10];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
  
  if (received)
    {
    if (buf [0] != 1)
      return;  // not my device
    
    if (buf [1] != 2)
      return;  // unknown command
    
    byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
    };
    
    delay (5);  // give the master a moment to prepare to receive
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    sendMsg (fWrite, msg, sizeof msg);
    digitalWrite (ENABLE_PIN, LOW);  // disable sending
    
    digitalWrite(LED_PIN, buf[2]);  // set LED level
   }  // end if something received
}  // end of loop

The Tiny85 has a limited number of pins, therefore I only used one for LED status display and that’s it.
It’s connected to the SN75176.
It’s only task is to receive the information, pass the value to the led and respond that the information was received.

On the other side, there is SN75176 connected to Arduino Duemilanove.
The code there follows:

#include "RS485_protocol.h"
#include <SoftwareSerial.h>


const byte ENABLE_PIN = 4;

SoftwareSerial rs485 (2, 3);  // receive pin, transmit pin

// callback routines
  
void fWrite (const byte what)
  {
  rs485.print (what);  
  }
  
int fAvailable ()
  {
  return rs485.available ();  
  }

int fRead ()
  {
  return rs485.read ();  
  }

void setup()
{
  pinMode(10, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(11, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(12, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(13, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(2,INPUT);   // rx pin
  pinMode(3,OUTPUT);  // tx pin
  rs485.begin (4800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  Serial.begin(9600); 
}  // end of setup
  
byte level = 0;

void loop()
{

  // assemble message
  byte msg [] = { 
     1,    // device 1
     2,    // turn light on
     level // to what level
  };

  
  // send to slave  
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  sendMsg (fWrite, msg, sizeof msg);
  digitalWrite (ENABLE_PIN, LOW);  // disable sending
  
  Serial.print("Toto som poslal: ");
  Serial.print(" ");
  Serial.print(msg[0]);
  Serial.print(" ");
  Serial.print(msg[1]);
  Serial.print(" ");
  Serial.print(msg[2]);
  Serial.println();
  
  // receive response  
  byte buf [10];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
  
  Serial.print("toto dorazilo: ");
  Serial.print("Received: ");
  Serial.print(received);
  Serial.print(" BUF: ");
  Serial.print(buf[0]);
  Serial.print(" ");
  Serial.print(buf[1]);
  Serial.print(" ");
  Serial.print(buf[2]);
  Serial.print(" ");
  Serial.print(buf[3]);
  Serial.println();
  
  level = !level;
    
  delay(2000);

}  // end of loop

As I am switching the Arduino also as ISP for the tiny, I had to set the GPIO 10-13 as inputs.
The idea of mine is, that the Arduino sends the message to turn ON and OFF the led on the Tiny85.
However, the communication is not working.
The communication is flowing - I put a LED between the A and B lines and it blinks every 2 seconds, so I know the Arduino is sending something.
However, there is no response.

Can anyone help me debug the code and find the problem? Could the replacement of the 485 chip by the SN75176 be any problem?

That's a really old chip; I used it in a design back in 1980. It is a good chip and should work. Just because your LED blinks, doesn't exactly mean it's sending the data you think it is. It just means it's trying to do something.

I haven't used any of the software implemented communication libraries. Have you tried getting any of the examples to work? That is where you need to start. Get an example working with your hardware; then you know that your hardware isn't the problem. Once you have that working, then you can modify/write code.

When you use the attiny, you have to run the "burn bootloader" once on each new chip. This sets the internal fuses, it does not actually burn a bootloader into the attiny. If you haven't done that, the data rate in the attiny isn't going to be what you think it is, i.e. you may have set it to 9600, but the data rate is really 4300. The 4300 is just a number; I don't know what the data rate would be if you haven't set the fuses correctly.

I'm guessing that you don't have any test equipment. You could try running two attiny chips at the same time. This would eliminate the possibility of the data rates not matching.

Download this free software based O-scope http://www.zeitnitz.de/Christian/scope_en. It's a start. You may want to order the Saleae logic analyzerhttp://www.aliexpress.com/item/Free-shipping-New-Arrival-Saleae-USB-Logic-Analyzer-24M-8CH-MCU-ARM-FPGA-DSP-debug-tool/514908347.html. $8 USD. If you're going to be doing very much with electronics they also sell a 16ch one that is really awesome @ about $45 USDhttp://www.aliexpress.com/item/1pcs-lot-Free-shipping-New-Arrival-Saleae-Logic16-saleae16-USB-Logic-Analyzer-100M-16CH-best-quality/667671473.html.

Without test equip, you really need to get your code working with two identical setups(two Uno's, two Nano's, two attiny45s, etc). I'm not sure when I can set up what you have with two Uno's. What time frame do you need this done by?

I've bought the Arduino Pro Mini atmega328 for $4 at aliexpress.com, you do need a USB to ttl converter ($2) to program the ProMini, but it doesn't need to stay connected to it once you have it working. I mention this because for that $4-5http://www.aliexpress.com/item/5pcs-lot-Pro-Mini-328-Mini-ATMEGA328-5V-16MHz-Free-Shipping-Dropshipping/714840800.html for the pro mini, it's already put together and tested, small, and has most of the functionality of the Uno.

Well, after hours of fiddling I have to sum up - not working for me :frowning:
What I tried:

  • I took the advice and set two ATtiny85 chips - one as master, one as slave, communication only from master to slave, simplest data message (address, value), no luck
  • I also took the second advice and used the example (changed analog to digital read/write as I only have a button available right now)
  • I removed the 485 chips in the middle and connected directly the RX to TX and vice versa, no luck either
  • with 485 chips removed, I let only the transmitting TX connected to the receiving RX, no other reconnection, again no luck
  • I coded a small monitor script for Arduino with 500us sample frequency (the communication speed for rs485 were set to 600 bauds), I put the TX pin on the interrupt 0 and made an CHANGE handler - I can see the communication is flowing and working (let’s call it very simple logic analyzer :slight_smile: )
    Preconditions:
    Both Tiny chips were set to use internal 8MHz frequency, both had bootloader burned, both had “lifesign” LED enabled on PIN 4 with 200ms blink to let me know the code burned correctly.
    I can try to burn the code with 1MHz internal clock as a final step I am unable to compile the SoftwareSerial library in case the 1MHz clock is used - from my perspective even 300baud communication speed on the bus is sufficient.
    Can anyone point me out to the right direction until the logical analyzer advised within the fairwindsii post arrive by mail? :slight_smile:

I started with two Uno's. I'm operating on 3 hours of sleep and not getting anywhere. So, I'll try it again tomorrow.

OK…I didn’t go to bed…however…
I changed your code a little just to make it easier to test things.
I used #defines for naming the rec and xmit pins, and the data rates for RS485 and serial monitor.
Change it in one place and the compiler takes care of the rest.

Serial monitor output:
Toto som poslal: 1 2 0
toto dorazilo: Received: 0 BUF: 27 243 252 8
Toto som poslal: 1 2 1
toto dorazilo: Received: 0 BUF: 27 243 252 8
Toto som poslal: 1 2 0
toto dorazilo: Received: 0 BUF: 27 243 252 8
Toto som poslal: 1 2 1
toto dorazilo: Received: 0 BUF: 27 243 252 8

This is your modified code:

//sancho uno      fairwindsii info  blue  serial port 23
#include "RS485_protocol.h"
#include <SoftwareSerial.h>
#define  REC_PIN  2          
#define  XMIT_PIN 3
#define  RS485_RATE  9600
#define  SERIAL_MONITOR_RATE  9600

const byte ENABLE_PIN = 4; 

SoftwareSerial rs485 (REC_PIN,XMIT_PIN);//(2, 3);  // receive pin, transmit pin

// callback routines
  
void fWrite (const byte what)
  {
  rs485.print (what);  
  }
  
int fAvailable ()
  {
  return rs485.available ();  
  }

int fRead ()
  {
  return rs485.read ();  
  }

void setup()
{
  pinMode(10, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(11, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(12, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(13, INPUT); //allow GPIO to not interfere with Tiny85
  pinMode(REC_PIN,INPUT);   // rx pin
  pinMode(XMIT_PIN,OUTPUT);  // tx pin
  rs485.begin (RS485_RATE);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  Serial.begin(SERIAL_MONITOR_RATE); 
}  // end of setup
  
byte level = 0;

void loop()
{

  // assemble message
  byte msg [] = { 
     1,    // device 1
     2,    // turn light on
     level // to what level
  };

  
  // send to slave  
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  sendMsg (fWrite, msg, sizeof msg);
  digitalWrite (ENABLE_PIN, LOW);  // disable sending
  
  Serial.print("Toto som poslal: ");
  Serial.print(" ");
  Serial.print(msg[0]);
  Serial.print(" ");
  Serial.print(msg[1]);
  Serial.print(" ");
  Serial.print(msg[2]);
  Serial.println();
  
  // receive response  
  byte buf [10];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
  
  Serial.print("toto dorazilo: ");
  Serial.print("Received: ");
  Serial.print(received);
  Serial.print(" BUF: ");
  Serial.print(buf[0]);
  Serial.print(" ");
  Serial.print(buf[1]);
  Serial.print(" ");
  Serial.print(buf[2]);
  Serial.print(" ");
  Serial.print(buf[3]);
  Serial.println();
  
  level = !level;
    
  delay(2000);

}  // end of loop

Pics of the logic analyzer capture.
One shows the full data packet
THe second one shows it expanded and you can see the ascii/hex info on the top .
The left hand column shows the pin that was monitored.
The bottom trace is the data that was received from the first RS485.

NOTE: Don’t try to match the Serial Monitor output to the packet. They were captured at different times.

Your code for the Uno board works. Well done.

I need sleep. I’ll work on the other part tomorrow.
Can you send me your schematic so we’re both on the same sheet of music?
Later, Mark