~ Networking and Communications.
~ post date: 2017.5.23, recent update: 2017.5.16
Up to this point, aside from visual information via LCD or light interactivity via serial communication, what happened on the board stayed on the board. Now seems like a proper time to broaden the horizon a bit. I am drawn to the visual quality of networking via light and thusly chose to dive into infrared driven networking.
Project files
Networking light : Twin PCB
I was introduced to a project by Enrico Bassi which uses two ATMega boards connected to IR Sensors and IR LEDs to network by reading pauses in IR light (low). Upon seeing it, I was immediately impressed with the visceral qualities of networking with light. It is visible to our eyes! And, we could control a light in our hands to communicate to a machine. Admittedly, low tech stuff here. Remote controls... Plus, technically, we cannot see the light, the red LED on Bassi's board is for human reference, the IR LED is invisible to us. Regardless, as a first project to get into networking, I am game.
I start by breaking down Bassi's work. This is an image from his Fab Academy page. I added the component labels. I would like to do the design part of this build quick. Get into the fabrication and coding and then circle knowledge back out. In my head, I plan to pull the components from the first boards I do and re-use them in more advanced boards, working my way up to fabricating my own special Fabkit. That in mind, I start making some mods to his board. The blue labels I plan to eliminate.
Upon investigating his code and build further, I believe this same thing could be accomplished with an ATTiny44. Indeed, in his post, Bassi mentions that he switched to the ATMega 328 early in his process because his testing code was inflated and later realized the 48 had enough memory for the final arduino sketch. Bassi's Eagle schematic:
I prepared a schematic of the IR Networking board using an ATTiny44.
I had an idea that I would pull all the off board connections to one header but I am not sure it is going to work. Either way, I do not think it is worth the time. I will return to the lab, double check the inventory for an IR sensor, customize the pads and split the header.
I redesigned the schematic with through holes for larger LEDs and the IR receiver I found in the Shanghai electronics market. Then I added an ISP header and another 2x2 for external power and RX/TX.
Outline and traces look like this in the computer.
Like this in my hand a few minutes later.
And like this soldered a little while later still.
I made a mistake when orientating the three pins for the IR sensor. I should have mirrored the pins. And to do that, I might have to rearrange the lights and sensor to accomplish the traces without any zero ohm resistors. For now, I need to keep moving forward.
Download project files
I will post links to resources I have found helpful here.
Testing the electrical components of the twins
Infrared light is invisible to the human eye, so it is a challenge to know if the infrared LED is operational. Further, the sensors need to be operationally tested. I setup some simple arduino sketches to confirm the board.
First, you need to match the ATtiny44 pins to Arduino speak. Look at this graphic and compare it to the schematic. The LED is on ATtiny 12, irLED 11 and irSensor 10. One, two, three. I will add those as integars.
Now, many cameras can see infared light. The front camera on newer iPhones is one such camera.
const int irPin = 2; // ATtiny44 11 void setup() { } void loop() { digitalWrite(irPin, HIGH); // Turn on the irLED } }
The camera needs to be aligned dead on which is not easy using a camera without a viewfinder. Nonetheless, both infrared LEDs are working.
Now I will check the sensor. For this test, I modified code from my IR sensor trials.
#include <SoftwareSerial.h> SoftwareSerial mySerial(0,7); // RX PA7 6, TX PA0 13 (flipped) // Using an ATTiny44 // Pin Configurations const int ledPin = 1; // PA1 12 const int irPin = 2; // PA2 11 const int irSensor = 3; // PA3 10 ADC3 int sensorValue = 0; // variable to store the value coming from the sensor void setup() { pinMode(ledPin, OUTPUT); pinMode(irPin, OUTPUT); pinMode(irSensor, INPUT); mySerial.begin(19200); // I set this to 19200 and arduino monitor to 2400 mySerial.println("Hello"); } void loop() { sensorValue = digitalRead(irSensor);// read IR sensor write to variable mySerial.print("sensor "); mySerial.println(sensorValue); // send reading to serial digitalWrite(ledPin, HIGH); // visually confirm read timing delay(60); // pause between readings (not too fast for stability) digitalWrite(ledPin, LOW); delay(60); }
I did not see any variation in the sensor readings when I pointed the infrared light at the board. When the sensor receives light, it should read a 0. Then I recalled Enrico Bassi had the same problem and he made code for transmitting infrared light at 36kHz.
I modified my transmitter board code with Bassi's 36kHz loop.
const int ledPin = 1; // ATtiny44 12 const int irPin = 2; // ATtiny44 11 const int irSensor = 3; // ATtiny44 10 long previousMillis = 0; //to calculate the interval of the blink without using a delay unsigned long currentMillis = 0; void setup() { pinMode(ledPin, OUTPUT); pinMode(irPin, OUTPUT); pinMode(irSensor, INPUT); } void loop() { while (currentMillis - previousMillis < 125) //LED on timing { IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } previousMillis = currentMillis; while (currentMillis - previousMillis < 125) //LED off timing { digitalWrite(ledPin, LOW); currentMillis = millis(); } previousMillis = currentMillis; //reset counter offset } void IR() //36kHz infrared transmitting { digitalWrite(irPin, HIGH); delayMicroseconds(20); digitalWrite(irPin, LOW); delayMicroseconds(7); }
Sensor one is confirmed. I am still having problems with understanding the board clock timing and how to adjust these numbers accordingly.
The second sensor is unresponsive. The soldering looks good. I thought it might be an issue with the smaller irLed, so I grabbed the air conditioning remote. The sensor worked so I think there must be a problem with the smaller irLed I used. No, I just realized I forgot to dictate the pinModes in the setups. Now everything is working much better. (I added the pinMode settings to the code above, whereas previously I was not using.)
Download project files
I will post links to resources I have found helpful here.
Infrared networking
Now I that I have a master-slave network setup, I would like to see if I can get the two boards to send signals back and forth autonomously.
First, I extended the sensor test sketch to react via its LED and the serial monitor. I kept the serial monitor so I could debug along the way. One board is transmitting IR signals at random intervals. The other board is checking the IR receiver and when a signal is sensed, it triggers the LED on the board to illuminate for a short time period, then resumes loking. First the code for the master:
//IR transmit; send a signal at a random interval const int ledPin = 1; // ATtiny44 12 const int irPin = 2; // ATtiny44 11 const int irSensor = 3; // ATtiny44 10 long previousMillis = 0; //to calculate the interval of the blink without using a delay unsigned long currentMillis = 0; unsigned long ran = 0; int interval = 125; void setup() { pinMode(ledPin, OUTPUT); pinMode(irPin, OUTPUT); pinMode(irSensor, INPUT); } void loop() { ran = random(500, 2000); while (currentMillis - previousMillis < interval) //LED on timing { IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } previousMillis = currentMillis; while (currentMillis - previousMillis < ran) //LED off timing { digitalWrite(ledPin, LOW); currentMillis = millis(); } previousMillis = currentMillis; //reset counter offset } void IR() //36kHz infrared transmitting { digitalWrite(irPin, HIGH); delayMicroseconds(20); digitalWrite(irPin, LOW); delayMicroseconds(7); }
And the code for the slave:
//IR receive; receive a signal and illuminate the LED for a brief time #include <SoftwareSerial.h> SoftwareSerial mySerial(0,7); // RX PA7 6, TX PA0 13 (flipped) // Using an ATTiny44 // Pin Configurations const int ledPin = 1; // PA1 12 const int irPin = 2; // PA2 11 const int irSensor = 3; // PA3 10 ADC3 int sensorValue = 0; // variable to store the value coming from the sensor int noSensorRead = 1; void setup() { pinMode(ledPin, OUTPUT); pinMode(irPin, OUTPUT); pinMode(irSensor, INPUT); mySerial.begin(19200); // I set this to 19200 and arduino monitor to 2400 mySerial.println("Hello"); } void loop() { sensorValue = digitalRead(irSensor);// read IR sensor write to variable mySerial.print("sensor "); mySerial.println(sensorValue); // send reading to serial //delay(10); if (sensorValue != noSensorRead) { // if IR sensed, pause reading and turn on LED digitalWrite(ledPin, HIGH); delay(1000); digitalWrite(ledPin, LOW); } else { delay(10); // pause between readings (not too fast for stability) } }
Next I will try to combine the two. As I customized this more, I needed to have a better understanding of what I was working with. millis() returns the number of milliseconds since the ATtiny44 board began running the current program. Unsigned long variables are extended size variables for number storage, and store 32 bits (4 bytes). Unlike standard longs unsigned longs won't store negative numbers, making their range from 0 to 4,294,967,295 (2^32 - 1). The only tricky bit in combining the two was getting my head around the millis() function, which is simple, if you take the time to find the information rather than making assumptions. In this way, functions can be run during a time interval, which probably would have been quite helpful a couple weeks ago when I was working with the phototransistor code variations.
//IR communication; receive a signal, send a signal, receive a signal, send a signal #include <SoftwareSerial.h> //SoftwareSerial mySerial(0,7); // RX PA7 6, TX PA0 13 (flipped) // Using an ATTiny44 // Pin Configurations const int ledPin = 1; // PA1 12 const int irPin = 2; // PA2 11 const int irSensor = 3; // PA3 10 ADC3 int sensorValue = 0; // variable to store the value coming from the sensor int noSensorRead = 1; unsigned long previousMillis = 0; //to calculate the interval of the blink without using a delay unsigned long currentMillis = 0; unsigned long ran = 125; int interval = 250; void setup() { pinMode(ledPin, OUTPUT); pinMode(irPin, OUTPUT); pinMode(irSensor, INPUT); //mySerial.begin(19200); // I set this to 19200 and arduino monitor to 2400 //mySerial.println("Hello"); } void loop() { ran = random(250, 2000); //generate a random interval sensorValue = digitalRead(irSensor);// read IR sensor write to variable //mySerial.print("sensor "); //mySerial.println(sensorValue); // send reading to serial if (sensorValue != noSensorRead) { // if IR sensed, pause reading and turn on LED digitalWrite(ledPin, HIGH); delay(interval); digitalWrite(ledPin, LOW); delay(ran); currentMillis = millis(); previousMillis = currentMillis; //set counter offset while (currentMillis - previousMillis < interval) { //LED on timing IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } digitalWrite(ledPin, LOW); } else { delay(10); // pause between readings (not too fast for stability) } } void IR() //36kHz infrared transmitting { digitalWrite(irPin, HIGH); delayMicroseconds(20); digitalWrite(irPin, LOW); delayMicroseconds(7); }
The code works well until there is an interruption and one of the signals is missed. The smaller infrared LED has a narrower projection field. If attempting to communicate over longer distances, or with less precision in alignment, I recommend going with the wider LED. On the otherhand, a simple code like this is disrupted by any IR signal, such as an air conditioning remote, so narrowing the field of view on the receiver and/ or increasing the accuracy of the IR LED may be necessary in some scenarios.
At the beginning of that video, I pressed the signal and GND pins of one IR sensor together to fake a signal. That is one problem that needs to be addressed, when a signal is missed, how can it be re-established? To address this, I made the whole loop operate within a timing interval that exceeds the random max. If no signal is received, the board knows it missed one and immediately attempts to reestablish the network.
//IR communication; receive a signal, send a signal, receive a signal, send a signal #include <SoftwareSerial.h> //SoftwareSerial mySerial(0,7); // RX PA7 6, TX PA0 13 (flipped) // Using an ATTiny44 // Pin Configurations const int ledPin = 1; // PA1 12 const int irPin = 2; // PA2 11 const int irSensor = 3; // PA3 10 ADC3 int sensorValue = 0; // variable to store the value coming from the sensor int noSensorRead = 1; unsigned long previousMillis = 0; //to calculate the interval of the blink without using a delay unsigned long currentMillis = 0; unsigned long ran = 125; int interval = 250; void setup() { pinMode(ledPin, OUTPUT); pinMode(irPin, OUTPUT); pinMode(irSensor, INPUT); //mySerial.begin(19200); // I set this to 19200 and arduino monitor to 2400 //mySerial.println("Hello"); } void loop() { currentMillis = millis(); previousMillis = currentMillis; while (currentMillis - previousMillis < interval) { // Reestablishing connection IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } digitalWrite(ledPin, LOW); previousMillis = currentMillis; //set counter offset while ( currentMillis - previousMillis < 2500) { // if the timing of this exceeds random max, reestablish sensorValue = digitalRead(irSensor); // read IR sensor write to variable //mySerial.print("sensor "); //mySerial.println(sensorValue); // send reading to serial if (sensorValue != noSensorRead) { // if IR sensed, pause reading and turn on LED digitalWrite(ledPin, HIGH); delay(interval); digitalWrite(ledPin, LOW); ran = random(250, 2000); //generate a random interval delay(ran); currentMillis = millis(); previousMillis = currentMillis; //set counter offset while (currentMillis - previousMillis < interval) { //LED on timing IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } digitalWrite(ledPin, LOW); previousMillis = currentMillis; //set counter offset } else { delay(10); // pause between readings (not too fast for stability) currentMillis = millis(); } } } void IR() //36kHz infrared transmitting { digitalWrite(irPin, HIGH); delayMicroseconds(20); digitalWrite(irPin, LOW); delayMicroseconds(7); }
Next I tried to setup a system where one board would first confirm a message was received, then wait a random interval, and send a new message. After an hour and some change of work, my mind is spinning and I do not quite have it working yet. Here is my current sketch.
//IR communication; receive a signal, confirm, send a signal, confirm, receive a signal //#include <SoftwareSerial.h> //SoftwareSerial mySerial(0,7); // RX PA7 6, TX PA0 13 (flipped) // Using an ATTiny44 // Pin Configurations const int ledPin = 1; // PA1 12 const int irPin = 2; // PA2 11 const int irSensor = 3; // PA3 10 ADC3 int sensorValue = 0; // variable to store the value coming from the sensor unsigned long previousMillis = 0; //to calculate the interval of the blink without using a delay unsigned long currentMillis = 0; unsigned long ran = 125; int interval = 250; void setup() { pinMode(ledPin, OUTPUT); pinMode(irPin, OUTPUT); pinMode(irSensor, INPUT); //mySerial.begin(19200); // I set this to 19200 and arduino monitor to 2400 //mySerial.println("Hello"); currentMillis = millis(); //reset timing previousMillis = currentMillis; while (currentMillis - previousMillis < interval) { //startup IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } digitalWrite(ledPin, LOW); } void loop() { sensorValue = digitalRead(irSensor); // read IR sensor write to variable //mySerial.print("sensor "); //mySerial.println(sensorValue); // send reading to serial if (sensorValue == HIGH) { // if IR sensed, respond, delay random, then send new while (sensorValue == LOW) { // wait until the entire signal was received //digitalWrite(ledPin, HIGH); delay(10); //in case of missed signals sensorValue = digitalRead(irSensor); } //digitalWrite(ledPin, LOW); //delay(10); //visual confirmation of transition from read to send currentMillis = millis(); //reset timing previousMillis = currentMillis; while (currentMillis - previousMillis < interval) { //confirm message receipt IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } digitalWrite(ledPin, LOW); ran = random(500, 1000); //generate a random interval then delay delay(ran); while (sensorValue == HIGH) { //send message and wait for confirm currentMillis = millis(); //reset timing previousMillis = currentMillis; while (currentMillis - previousMillis < interval) { // send a message IR(); digitalWrite(ledPin, HIGH); currentMillis = millis(); } digitalWrite(ledPin, LOW); //delay(20); //give other board a chance to respond currentMillis = millis(); //reset timing previousMillis = currentMillis; while (sensorValue == HIGH && currentMillis - previousMillis < interval*1.1) { // check message receipt sensorValue = digitalRead(irSensor); } } delay(200); // time for the signal to be sent before returning to sensing } else { delay(10); // pause between readings (not too fast for stability) } } void IR() //36kHz infrared transmitting { digitalWrite(irPin, HIGH); delayMicroseconds(20); digitalWrite(irPin, LOW); delayMicroseconds(7); }
Download project files
I will post links to resources I have found helpful here.
Jump : Index
J.travis Russett © 2017
All the work contained within is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) License
You may remix, tweak, and build upon my work non-commercially, as long as you credit me and license your new creations under the identical terms.