Hotfix release available: 2014-09-29d "Hrun". upgrade now! [46.4] (what's this?)
Hotfix release available: 2014-09-29c "Hrun". upgrade now! [46.3] (what's this?)

Networking and Communications

Goal

design and build a wired &/or wireless network connecting at least two processors

Design

With Francisco's recommendation, I installed Kokopelli this week to test it out. However it didn't went well. I copied the code in Week 13 but they did not work out of the box. Many variables and functions were undefined. I think I might miss some libraries.

Later Francisco told me it was a version issue.


Eventually I used my old friend Fritzing for this assignment. My design is to make an input board with sound sensor and an output board with a motor. When there is a short sound, the motor rotates; where is a long sound, the motor rotates in the opposite direction.

I drew a board for output device and another for input device. Both of them have a Attiny 45 and two pins reserved for I2C or TX/RX for networking. The output board has a 20 Mhz oscillator and a PWM pin for motor. Input device has two pins for two inputs.

You may download layout Fritzing file: week14_i2c_input.fzz, week14_i2c_output.fzz

Programming

Algorithm

Programming is my expertise and therefore it does not take me much time. The input board tells the output board which direction to rotate by sending a byte of either 0 or 180. The output board sends the same signal to the motor repeatedly for a second. The real gold here is how to differentiate short and long sounds. To do this, input board will set a boolean value when it receives the first sound, and then it delays 300ms to avoid a short sound to be detected more than once. And then it waits for a second sound for the next 800ms, In my practice, it checks 80 times with a delay of 10ms, which is crucial for timing. If there comes a second sound, it rotates. If not, it rotates reversely.

Connection

At first I tried to use TinyWireS, I2C libraries for attiny 45/85, but never succeeded. I spent many hours and eventually called for help on Fab Academy mail list. I got a friendly response from Till Cremer saying that the library was buggy. Also Neil and Bas Withagen provided alternative solutions with SoftwareSerial library. So I went with it.

There were a few different functions and optional arguments you could use in serial networking. I did not make clear how they worked and keep seeing wrongly-encoded text on my laptop. Here are some tips:

  • Serial.write: send data in bytes (When sending a byte 0, it send 8 bits of 0.)
  • Serial.print: send data in ASCII code (When sending a byte 0, it sends a char 0 in ASCII, which is 48 in decimal.) Optionally you can give a second argument such as DEC, HEX or BIN for representation.
  • Serial.read: read in one byte.

Thumb of rules: Always use Serial.write and unless the message is for humans to read. To test for connection, Arduino has a serial monitor that is very handy for debugging.

The following code is for Attiny45 to send signals to two processors when detecting sounds.

#include <SoftwareSerial.h>
//#include <TinyWireM.h>
#define FORWARD (byte)0
#define BACKWARD (byte)180
#define STOP (byte)90

SoftwareSerial mySerial0(1, 0);   // RX, TX
SoftwareSerial mySerial2(4, 2);   // RX, TX
int sensorPin_A = 3;                    //Microphone Sensor Pin on analog 3
int sensorValue_A = 0;
bool firstSound = false;
int counter = 0;
void setup() {
  //TinyWireM.begin();               // TinyWireM library does not work.
  mySerial0.begin(4800);
  mySerial2.begin(4800);
  mySerial0.write(FORWARD); //say hello
  mySerial2.write(FORWARD); //say hello
}
void loop() {  
  sensorValue_A = (int)analogRead(sensorPin_A);     // sensorValue_A ranges from 0 to 1023
  if ( sensorValue_A > 30 ){                    //The 'silence' sensor value is about 28 or 29
      if (firstSound) {                       // if this is a second sound, move backward.
        mySerial0.write(BACKWARD);
        mySerial2.write(BACKWARD);
        firstSound = false;
        counter = 0;
        delay(2000);
      }
      else {             // if this is the first sound, wait and see if there is a second one.
        firstSound = true;
        counter = 0;
        delay(300);
      } 
  } else {
      if (counter > 80 && firstSound) {    // when there is a first sound followed by nothing, move forward
          mySerial0.write(FORWARD);
          mySerial2.write(FORWARD);
          firstSound = false;
          counter = 0;
          delay(2000); 
      } 
  }
  delay(10);                      // avoid repeat
  counter++;                    // counter tells how many loops pass after the first sound.
}

The following code is for Attiny45 to receive signals and to rotate the servos.

#include <SoftwareServo.h> 
#include <SoftwareSerial.h>
//#include <TinyWireS.h>
#define FORWARD (byte)0
#define BACKWARD (byte)180
#define STOP (byte)90

SoftwareServo myservo;         // create servo object to control a servo 
int pos = 0;                   // variable to store the servo position 
byte inByte = STOP;
SoftwareSerial mySerial(0, 2); // RX, TX
void setup() {
  // TinyWireS.begin();        // TinyWireS library does not work.
  myservo.attach(1);           // attaches the servo on pin 1 to the servo object 
  turn(15);                    // A 'Hello' to you
  mySerial.begin(4800);
}
void loop() {
  if (mySerial.available()) {
    inByte = mySerial.read();
    if (inByte == FORWARD) {
      motorMove(15, FORWARD);
    } 
    else if ( inByte == BACKWARD ) {
      motorMove(15, BACKWARD);
    }
    mySerial.write(inByte);
  }
}
void motorMove(int moveTime, byte moveDirection) {
  //for(pos = moveDirection; pos != 90 ; pos < 90 ? pos ++ : pos -- )  
  int moveD=0;
  for(pos = 0; pos < moveTime ; pos++ ) 
  {   
    if(pos < 10) {   
        moveD = moveDirection > 90 ? STOP + pos : STOP - 10;
    } else {
        moveD = moveDirection;
    }
    myservo.write(moveDirection);              
    delay(10);                      
    SoftwareServo::refresh();
  } 
}
void turn(int delayTime) {
  for(pos = 0; pos < 180; pos++)        // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);                    // tell servo to go to position in variable 'pos' 
    delay(delayTime);                       // waits 15ms for the servo to reach the position 
    SoftwareServo::refresh();
  } 
  for(pos = 180; pos>= 0; pos--)         // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);                    // tell servo to go to position in variable 'pos' 
    delay(delayTime);                       // waits 15ms for the servo to reach the position 
    SoftwareServo::refresh();
  } 
}
Or watch the video at Youtube