CLUNET library - simple single-wire peer-to-peer network driver for AVR microcontrollers, perfect way to interconnect microcontrollers in your house
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


The CLUNET library - is a simple single-wire peer-to-peer network driver for AVR microcontrollers, perfect way to interconnect microcontrollers in your house.


  • Requires only a few cheap additional components;
  • Using only a single wire;
  • Using only two pins;
  • There is no master device, all devices are equal;
  • You can use a very long cable, more than 100 meters;
  • You don't need to care about collisions;
  • Automatic CRC calculation and CRC check;
  • Pseudo multitasking using interrupts;
  • Up to 255 devices on one bus.

How to use

Hardware part

Sample schematic

You need a pin with an external interrupt to read data and any other pin to send data. Transistor method is recommended.

Software part

You will need one free 8-bit timer with output compare match interrupt.


Edit clunet_config.h and change some values:

  • CLUNET_DEVICE_ID - address of the device (0-254);
  • CLUNET_DEVICE_NAME - name of the device (optional);
  • CLUNET_SEND_BUFFER_SIZE - send buffer size and maximum output packet size (memory usage);
  • CLUNET_READ_BUFFER_SIZE - read buffer size and maximum input packet size (memory usage);
  • CLUNET_WRITE_PORT and CLUNET_WRITE_PIN - port and pin to send data;
  • CLUNET_READ_PORT and CLUNET_READ_PIN - port and pin to read data, should be connected directly to the line;
  • CLUNET_WRITE_TRANSISTOR - define if you are using a transistor on output pin (see schematics);
  • CLUNET_TIMER_PRESCALER - prescaler for timer, define it to autocalculate CLUNET_T value as 64us, it must match timer initialization code, (F_CPU / CLUNET_TIMER_PRESCALER) / 15625 must be >= 8 and <= 24, thus 64 is the optimal value for 8MHz;
  • CLUNET_T - T value, it's a time period between signals in timer ticks, length of logical 0 is T, and logical 1 is 3*T, lower T is faster while higher T is more stable, it's autocalculated as 64us based on CLUNET_TIMER_PRESCALER if not defined;
  • CLUNET_TIMER_INIT - code to init timer (normal mode);
  • CLUNET_TIMER_REG - timer/counter register;
  • CLUNET_TIMER_REG_OCR - timer/counter output compare register;
  • CLUNET_ENABLE_TIMER_COMP and CLUNET_DISABLE_TIMER_COMP - code to enable/disable timer output compare match interrupt;
  • CLUNET_ENABLE_TIMER_OVF and CLUNET_DISABLE_TIMER_OVF - code to enable/disable timer overflow interrupt;
  • CLUNET_INIT_INT - code to init external interrupt (read pin);
  • CLUNET_TIMER_COMP_VECTOR - timer output compare match interrupt vector;
  • CLUNET_TIMER_OVF_VECTOR - timer overflow interrupt vector`;
  • CLUNET_INT_VECTOR - external interrupt vector.

Default configuration file is optimised for ATMEGA8 / 8MHz.


Include "clunet.h" and create callback function to receive data:

#include "clunet.h"

void data_received(unsigned char src_address, unsigned char dst_address, unsigned char command, char* data, unsigned char size)
	/* your code here */

Add initialization code and enable interrupts:

int main (void)

Also you can use clunet_set_on_data_received_sniff() to set callback function which will receive all packets, not only for this device.

Function to send data:

void clunet_send(unsigned char address, unsigned char prio, unsigned char command, char* data, unsigned char size);
  • address - address of destination device or CLUNET_BROADCAST_ADDRESS for multicast
  • prio - packet priority:
  • command - command ID (0-255), note that some IDs are occupied by predefined commands (see clunet.h) and some are reserved (see below);
  • data - pointer to data if any;
  • size - data size.

Sample code:

char buffer[1];
buffer[0] = 1;

while (clunet_ready_to_send()); // wait while sending, otherwise next call will replace output buffer
                                // clunet_ready_to_send() returns current task priority
                                // or 0 if output buffer is not busy

char *hello = "Hello world!";
clunet_send(1, CLUNET_PRIORITY_INFO, 100, hello, strlen(hello));

Reserved commands

There are some reserved commands:

  • CLUNET_COMMAND_DISCOVERY (0x00) - send this command as broadcast packet to find all devices in your network, devices will answer with command CLUNET_COMMAND_DISCOVERY_RESPONSE (0x01) and CLUNET_DEVICE_NAME in data section;
  • CLUNET_COMMAND_REBOOT (0x02) - send this command to reboot a device;
  • CLUNET_COMMAND_BOOT_CONTROL - (0x03) reserved for bootloader and firmware update;
  • CLUNET_COMMAND_BOOT_COMPLETED (0x04) - sent by device on start (call of clunet_init()), data is value of MCUCSR register, so your can determine reset source;
  • CLUNET_COMMAND_PING (0xFE) - ping, you can test line using this command, device(s) will answer with CLUNET_COMMAND_PING_REPLY (0xFF) and same data.

Tested on

  • ATMEGA8;
  • ATMEGA16;
  • ATMEGA64.

Known bugs/problems

  • Any data on bus will slow down every connected device a little.
  • No delivery check, you can make it in your application code if necessary.


Alexey 'Cluster' Avdyukhin