Receiving ethernet frames on an 8-bit AVR microcontroller

Pieter-Tjerk de Boer, PA3FWM pa3fwm@amsat.org

This page describes a way to receive 10 Mbit/s ethernet frames on an AVR microcontroller, using "bit-banging", i.e. without using a separate ethernet chip (such as the ENC28J60 used in many other projects). As such, it's an extension of Igor Češko's "IgorPlug-UDP" project, which transmits UDP/IP packets over ethernet, with 1 byte of cleverly coded user data.

Principle

10-Mbit/s ethernet uses Manchester encoding, meaning that for every bit, a high and a low level (positive and negative voltage on the twisted-pair cable) are sent, with the order of the two determining whether it's a 1 or a 0 bit. Thus, the signal can change up to 20 million times per second. Transmitting such a signal is just feasible by bit-banging, if the microcontroller is clocked at 20 MHz, by writing code consisting of nothing but a long row of out instructions.

However, receiving the signal this way is not possible. Clocked at 20 MHz, the microcontroller can sample the signal (using in instructions) "only" 20 million times per second, which is not fast enough to sample a signal that may be changing up to 20 million times per second (i.e., a frequency of 10 MHz). This is because there is no guarantee how the sampling moments (driven by the microcontroller's clock) relate to the moments the signal changes: if the sampling moment happen to be just when the signal is changing from high to low or vice versa, it can't be sampled reliably.

However, some ethernet signals (i.e., some data patterns) can be sampled sufficiently fast. If the ethernet data is alternately 0 and 1, then the signal will be say first high then low for the 0, followed by first low then high for the 1, and so on; the resulting frequency is just 5 MHz. Sampling 20 million times per second is more than sufficient for a 5 MHz signal. So, by restricting ourselves to packets with long stretches of alternating 0 and 1, the signal can be sampled sufficiently often by the microcontroller. And we can still transport some information by inverting the 0/1 pattern within the packet, effectively applying binary phase shift keying (BPSK) to the 5 MHz signal. In the end, my implementation can transport 20 bytes of user data per ethernet frame, and this could probably be increased further if needed.

Of course, my approach is limited to specially crafted packets, so cannot be used as e.g. a webserver. Also, since the receiver code cannot check the frame's address field, there is a risk that packets destined for some other host are misinterpreted by this receiver, so one should do some sanity-checking of the received data (e.g., include a specific pattern in some of the 20 user data bytes) to minimize this risk.

Practical use

I actually didn't do this just for fun, but also with a practical application in mind: remotely turning my PC on and off. Of course, there exists "Wake-On-Lan", but that's rather hard to use from outside one's own local network; one needs some support in one's modem/router, which mine didn't have. Now I have the AVR patiently waiting for commands, powered from the modem/router, ready to switch on the PC via a relay when it is sent the precisely right packet.

To make this work, I had to configure the modem/router to assign my AVR a fixed IP address, and configure a port forwarding for UDP packets to this address. Furthermore, the AVR has been programmed to send (potentially gratuitous) ARP replies whenever it sees some activity, just in case that activity is an ARP request from the modem/router.

My code and schematic

I would have liked to publish the entire source code for this project under some open-source licence like GPL. However, my code relies heavily on Igor Češko's transmit code; without the ability to transmit at least ARP replies, my receive code would be rather useless. Also, I improved Igor's code in a few points. Igor's code does not come with any clear licence, only with a copyright statement. Strictly speaking, that means I'm not allowed to (re)distribute his code. I tried to contact Igor about this, but all e-mail addresses I can find for him bounce. Unable to contact him, I've decided to still publish my code, including the modifications of his code. Since his code was already published (on his own website), this doesn't change the public-ness of his code, so I assume/hope he doesn't mind. The result however cannot be licensed under an open-source licence without his permission.

Here's the download link: avreth-0.02.tgz. This is a demonstration code: it simply sends back the received data bytes. It can be used as a starting point to code your own applications.

Oh, and here's the (rather trivial) schematic. [schematic] The receive wire pair (RxPlus/RxMin) is terminated in 2 times 68 ohms, and the DC level is put slightly higher than half the power supply voltage, so any (AC) signal coming in will toggle the PD7 input.

It should be noted that this way of connecting to an ethernet cable violates all standards; it may produce RFI (noise on nearby radios), be susceptible to radio signals (from nearby transmitters such as cellphones or wifi), and even destroy things, particularly if connected to an ethernet port carrying Power-over-Ethernet.

Alternative

Months after completing this project, I found Charles Lohr's ethertiny project, which manages to transmit and receive arbitrary ethernet frames, albeit using a totally different approach: he overclocks the CPU to more than 30 MHz, and uses his AtTiny85's Universal Serial Interface to read and write the data at full speed, without needing I/O instructions in each individual clock cycle.
Text and pictures on this page are copyright 2015, P.T. de Boer, pa3fwm@amsat.org .
Republication is only allowed with my explicit permission.