Showing posts with label Arduino Nano. Show all posts
Showing posts with label Arduino Nano. Show all posts

2019/05/25

Light Tracking Gimbal



Preface

Based on my previous project (Joystick Control Gimbal), I slightly modified it into a Light Tracking device.  It took me about two days to fine-tune the Light Tracking Gimbal to move smoothly and hold its position while light source is not moving.

Goal of this tutorial

Let's see the video below to see what's the final result of this tutorial.




Gimbal Panel Area

There are Top Left, Top Right, Bottom Left, and Bottom Right.
In each area, there is a light sensor.



Programming Logic:

  1. Get reading from 4 photo sensors 
    • I sample each sensor 10 times and then get the average value to ensure the readings are more stable
  2. Get average of each side
    • Top side value = (Top left + Top right) / 2
    • Bottom side value = (Bottom left + Bottom right) / 2
    • Left side value = (Top left + Bottom left ) / 2
    • Right side value = (Top right + Bottom right ) / 2
  3. Compare all four sides to see which side has bigger value
  4. Move the servo towards the biggest side among four side
  5. Tolerance variable is in control whether to move or not based on how much differences among four sides. If reading values from four sides were within the range of 100, then keep the Gimbal static(Hold it's position) (default value = 100)
Tolerance variable is the most important variable to ensure the Gimbal holds its position when four light sensors get values within the defined tolerance!

Component Needed for this project

  • Arduino Nano * 1
  • SG90 Servo * 2
  • Light sensors * 4
  • 10K Resistors * 4
  • Prontoboard * 1
  • Many jump wires
  • Extra long wires for photo sensor connect * 4
  • Heat shrink tube 2cm * 4

Tools Needed

  • Soldering gun * 1
  • Diagonal cutting pliers * 1
  • Screw driver * 1
  • Few screws * 10
  • Double sided tape

Schematic: (I use fritzing for this schematic)



Program Code:

*************************************************************
*** Please click subscribe my YouTube Channel before use the code 
*** There is no restriction, but I would be really appreciated 
*** if you did. Thank you!  **************************************************************

*************************************************************
*** 在 Copy 底下程式碼之前,請按下底下按鈕,訂閱我的 YouTube 頻道! 
*** 雖沒有強制規定,不過如果你有訂閱的話,非常感謝你! 
**************************************************************
/**
   Author: Kevin Chen AKA Stonez56 
   My Blog to see more tutorials: https://stonez56.blogspot.com
   Date: 2019/05/11 The day before mother's day

    Program function flow
   1. Reading four light sensor values
   2. Average top two(Top left, Top right), bottom two(Bottom left, Bottom right)
   3. Average left two(Top left, Bottom left), right two (Top right, Bottom right)
   4. Compare 4 averaged value and make servo mave toward the biggest number side
   
   v0 Light traking Gimbal base to get readings
   v1 Write servo code in and move servo accordingly

                    |
         Top left   |     Top right
   -----------------+----------------------
     Bottom left   |  Bottom right
                    |
*/
#include <Servo.h>
Servo servo1_x;
Servo servo2_y;

//Light Sensor Pin definitions
const uint8_t  light_top_left_PIN = A6;
const uint8_t  light_bottom_left_PIN = A5;
const uint8_t  light_top_rigth_PIN = A2;
const uint8_t  light_bottom_right_PIN = A1;
//Potentiometer pin
const  int potPIN = A0;
//Servo pins
const int servo1_x_pin = 2;
const int servo2_y_pin = 4;


//User define variables
// Gimbal movement tolerance 50~255
byte gimbal_movement_tolerance = 100;
//Photo sensor max reading times for average (for more accuracy)
byte max_reading = 10;
// define original servo angle
uint8_t originalAngle = 92;
uint8_t x_last = originalAngle;  // X last postion
uint8_t y_last = originalAngle; // Y last postion
uint8_t moveSpeed = 10;  //How fast show this Servo move
uint8_t maxSpeed = 50; //Max speed
uint8_t minSpeed = 1;
uint8_t y_minAngle = 1;  //Mimum angle
uint8_t y_maxAngle = 180;  //Maximum angle
uint8_t x_minAngle = 90;  //Mimum angle
uint8_t x_maxAngle = 180;  //Maximum angle

void setup() {
  Serial.begin(57600);

  pinMode(light_top_left_PIN, INPUT);
  pinMode(light_bottom_left_PIN, INPUT);
  pinMode(light_top_rigth_PIN, INPUT);
  pinMode(light_bottom_right_PIN, INPUT);

  servo1_x.attach(servo1_x_pin);
  servo1_x.write(originalAngle); //move servo to defined angle
  servo2_y.attach(servo2_y_pin);
  servo2_y.write(originalAngle); //move servo to defined angle

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

}

void loop() {
  char moveTowards = ' ';


  int potValue = analogRead(potPIN);
  Serial.print(F("potValue: "));
  Serial.println(potValue);
  moveSpeed = map(potValue, 0, 500, minSpeed, maxSpeed);
  Serial.print(F("moveSpeed: "));
  Serial.println(moveSpeed);
  moveTowards = getFacingToward(); //find out which side to move


//  Serial.print(F("moveTowards: "));
//  Serial.println(moveTowards);
  moveServo(moveTowards);

  delay(80);
}

void moveServo(char moveTowards) {


  //previous position were stored in x_last, y_last
  switch (moveTowards) {
    case 'T': //Move towards top
      if (x_last - moveSpeed < x_minAngle) {
        x_last = x_minAngle;
        servo1_x.write(x_last);
      } else {
        x_last -= moveSpeed;
        servo1_x.write(x_last);
      }
      break;
    case 'B': //Move towards bottom
      if (x_last + moveSpeed > x_maxAngle) {
        x_last = x_maxAngle;
        servo1_x.write(x_last);
      } else {
        x_last += moveSpeed;
        servo1_x.write(x_last);
      }
      break;
    case 'L': //Move towards left
      if (y_last - moveSpeed < y_minAngle) {
        y_last = y_minAngle;
        servo2_y.write(y_last);
      } else {
        y_last -= moveSpeed;
        servo2_y.write(y_last);
      }
      break;
    case 'R':
      if (y_last + moveSpeed > y_maxAngle) {
        y_last = y_maxAngle;
        servo2_y.write(y_last);
      } else {
        y_last += moveSpeed;
        servo2_y.write(y_last);
      }
      break;
    default:
      //Don't move servo
      break;
  }

}

/**
   This fuction get reading max_reading times and return average
   if it sees an 0, it will skip it
*/
int averageReading(int PIN, byte max_reading) {
  int total = 0;
  int current = 0;

  for (byte i = 0; i <= max_reading; i++) {
    current = analogRead(PIN);
    if (current == 0) {
      current = analogRead(PIN);
    }
    total += current;
  }

  return total / (max_reading);
}

/**


   Parameters: N/A
   Return: char; T, B, R, L to indicate the moving directoin
                       '-' means not moving at all
*/
char getFacingToward() {
  //1. Read each pin max_reading times

  int top_left = averageReading(light_top_left_PIN, max_reading);
  int bottom_left = averageReading(light_bottom_left_PIN, max_reading);
  int top_right = averageReading(light_top_rigth_PIN, max_reading);
  int bottom_right = averageReading(light_bottom_right_PIN, max_reading * 3);

  //Show photo sensor readings...
  Serial.print(F("Top left-A6: "));
  Serial.println(top_left);
  Serial.print(F("Bottom left-A5: "));
  Serial.println(bottom_left);
  Serial.print(F("Top right-A2: "));
  Serial.println(top_right);
  Serial.print(F("Bottom rightA1: "));
  Serial.println(bottom_right);

  //2. Get max value sides(two averaged, see above)
  byte go_direction[4] = {0, 0, 0, 0};
  int toward[4] = {0, 0, 0, 0}; //Top, Bottom, Left, Right
  toward[0] = (top_left + top_right) / 2 ;
  toward[1] = (bottom_left + bottom_right) / 2 ;
  toward[2] = (top_left + bottom_left) / 2;
  toward[3] = (top_right + bottom_right) / 2;

  //3. Add all side and average,
  //    if each side is within the tolerance then don't move Gimbal
  //    average = toward[0] +...toward[3]
  //    average - toward[0] .... toward[3], if all within gimbal_movement_tolerance, then no move
  int total_toward = 0;
  total_toward += toward[0];
  total_toward += toward[1];
  total_toward += toward[2];
  total_toward += toward[3];
  //get average
  int total_average = total_toward / 4; //4 sides
  Serial.print(F("total_average:"));
  Serial.println(total_average);
  //    if each side is within the tolerance then don't move Gimbal
  boolean shouldMove = false;
  //else move the gimbal
  if (total_average - toward[0] > gimbal_movement_tolerance) shouldMove = true;
  if (total_average - toward[1] > gimbal_movement_tolerance) shouldMove = true;
  if (total_average - toward[2] > gimbal_movement_tolerance) shouldMove = true;
  if (total_average - toward[3] > gimbal_movement_tolerance) shouldMove = true;

  Serial.print(F("toward 0 TOP : "));
  Serial.println(toward[0]);
  Serial.print(F("toward 1 BOTTOM: "));
  Serial.println(toward[1]);
  Serial.print(F("toward 2 LEFT: "));
  Serial.println(toward[2]);
  Serial.print(F("toward 3 RIGHT: "));
  Serial.println(toward[3]);

  //Find the biggest number to decide which side to go,
  // but if four values are quite similar, send '-' back to indicate not moving
  char facing = ' ' ;
  if (shouldMove) {
    int max_ = 0;
    if (toward[0] > max_) {
      max_ = toward[0];
      facing = 'T';
    }
    if (toward[1] > max_) {
      max_ = toward[1];
      facing = 'B';
    }
    if (toward[2] > max_) {
      max_ = toward[2];
      facing = 'L';
    }
    if (toward[3] > max_) {
      max_ = toward[3];
      facing = 'R';
    }
  } else {
    facing = '-'; //no need to move
  }
  //  Serial.print(F("shouldMove: "));
  //  Serial.println(shouldMove);
  return facing;
}



References:

  1. Serveo Example: https://www.instructables.com/id/4-Simple-Servo-Project-with-Arduino/ 
  2. Light Sensor Example: https://maker.pro/arduino/tutorial/how-to-use-an-ldr-sensor-with-arduino 
  3. 3D Gimbal base model: https://www.thingiverse.com/thing:2892903
    You probably need slight modification to this model to be able to fix the base... I did
====================THE END============

中文版


前言

根據我之前做的專案(Arduino 用搖桿控制的雙向轉軸的平台),我稍微將它修改為一個光源追蹤裝置。 主要的時間是花在微調在雲台伺服器的控制上,以便它光源不動的情況下,平穩移動並保持其位置。

專案目標

讓我們看一下下面的成果影片,看看本次教學的最終成果:


雲台分區

分為左上角,右上角,左下角,右下角,每一個分區內,各有一顆光敏電阻。


程式邏輯

  1. 從4個光敏電阻讀取訊號數值:
    • 為了得到了更穩定的數值,每個光敏電阻採樣10次,然後取得到平均值。
  2. 取得到每一方向的平均值
    • 上方值=(左上角+右上角)/ 2
    • 下方值=(左下角+右下角)/ 2
    • 左邊值=(左上角+左下角)/ 2
    • 右邊值=(右上角+右下角)/ 2
  3. 比較所有四個方向面,找出更大的值的方向
  4. 將伺服機雲台朝向向大的值方向移動
  5. Tolerance variable 根據四邊之間的差異來控制是否移動。如果四個方向的值大小在 100以內,則伺服機雲台保持不動。(默認值= 100)

所需零件

  • Arduino Nano * 1
  • SG90 伺服馬達 * 2
  • 光敏電阻 * 4
  • 10K 電阻* 4
  • 洞洞板 * 1
  • 許多單蕊線
  • 稍長一些的單蕊線(接光敏電阻用) * 4
  • 熱縮套管 2cm * 4

所需工具

  • 焊鎗工具 * 1
  • 斜口鉗 * 1
  • 縲絲刀 * 1
  • 小縲絲 * 10
  • 雙面膠

線路圖: (我是用 fritzing 工具製作)



程式碼:

請參考英文版上方。

參考資料:

  1. 伺服器: https://www.instructables.com/id/4-Simple-Servo-Project-with-Arduino/ 
  2. 光敏電阻: https://maker.pro/arduino/tutorial/how-to-use-an-ldr-sensor-with-arduino 
  3. 3D 雲台模型: https://www.thingiverse.com/thing:2892903
    個雲台的底座螺絲孔過大,導致 SG90 所附螺絲無法直接鎖緊。要請高手出來修改一下! 

全文完

2019/05/04

A Pan Tilt Gimbal w/ Joystick




In this Arduino tutorial, I will show you how to make A Pan Titl Gimbal with Joystick; plus speed control.  It's quite simple.

This Gimbal base is controlled by the joystick and the potentiometer is used to control how fast the SG90 servo would move.

3D printed Gimbal




Schematic:




In the video, as I turn the potentiometer, the Serial port shows the speed is increased to 9.  You will see the Gimbal base is moving must faster. Also, this video shows you how fast the Gimbal is moving based on the potentiometer value.

Watch the Video:



 Components: 

  •  One Arduino Nano 
  •  One Joystick module 
  •  One Potentiometer 
  •  Two SG90 servo 
  •  3D printed Gimbal base

Wire Connections:

  •  Connect joystick GND, 5V, VRX to A4, VRX to A3 and SW to D6 pins to Arduino.
  •  Connect 1st SG90 servo VCC to 5V, D2, and GND to Arduino 
  •  Connect 2nd SG90 servo VCC to 5V, D4, and GND to Arduino 
  •  Connect potentiometer VCC to 5V, A0, and GND to Arduino 

That's it! I hope you enjoy this tutorial, Have fun!
I'm thinking to extend this project to .... Light tracing Gimbal base, good idea?

Maybe you will be interested in my other projects:


References:


 Source codes:

*************************************************************
*** Please click subscribe my YouTube Channel before use the code 
*** There is no restriction, but I would be really appreciated 
*** if you did. Thank you!  **************************************************************

*************************************************************
*** 在 Copy 底下程式碼之前,請按下底下按鈕,訂閱我的 YouTube 頻道! 
*** 雖沒有強制規定,不過如果你有訂閱的話,非常感謝你! 
**************************************************************

/**
  2019-04-07 V0.4 by Stonez56

  Code from: https://stonez56.blogspot.com

  v0.4 Fix a bug to allow servos to reach the max limited angle
  v0.3 Added potentiometer to adjust speed (2019-04-28)
  v0.2 Made SG90 server stays there once hand released from Joystick (2019-04-28)
  v0.1 This Gimbal Servo code  moves SG90 servers to position (2019-04-07)



  Hint:  If x or y > 512 move to the one side, or x or y <= 512 move to the other side
*/

#include <Servo.h>
Servo servo1_x;
Servo servo2_y;

//Pin definitions
const  int potPIN = A0;  //potentiometer pin14
const int servo1_x_pin = 2;
const int servo2_y_pin = 4;
const int joystick_s_pin = 6;
const int joystick_x_pin = A3;
const int joystick_y_pin = A4;
const uint8_t  joystick_tolerance = 5;
uint8_t  joystick_center = 520;

//User define variables
uint8_t originalAngle = 92;
uint8_t x_last = originalAngle;  // X last postion
uint8_t y_last = originalAngle; // Y last postion
uint8_t moveSpeed = 10;  //How fast show this Servo move
uint8_t maxSpeed = 50; //Max speed
uint8_t minSpeed = 1;

uint8_t y_minAngle = 1;  //Mimum angle
uint8_t y_maxAngle = 180;  //Maximum angle

uint8_t x_minAngle = 90;  //Mimum angle
uint8_t x_maxAngle = 180;  //Maximum angle



void setup() {
  Serial.begin(57600);
  servo1_x.attach(servo1_x_pin);
  servo1_x.write(originalAngle); //place it servo in the middle
  servo2_y.attach(servo2_y_pin);
  servo2_y.write(originalAngle); //place it servo in the

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  pinMode(joystick_x_pin, INPUT);
  pinMode(joystick_y_pin, INPUT);
  pinMode(joystick_s_pin, INPUT_PULLUP);

}

void loop() {
  uint8_t x = 0;
  uint8_t y = 0;
  uint8_t button = 0;
  print_joystick_info();

  //Get readings from Joystick
  x = analogRead(joystick_x_pin);
  y = analogRead(joystick_y_pin);
  button = digitalRead(joystick_s_pin);

  int potValue = analogRead(potPIN);
  moveSpeed = map(potValue, 0, 560, minSpeed, maxSpeed);
  //  Serial.print("potValue: ");
  //  Serial.println(potValue);
  Serial.print("moveSpeed: ");
  Serial.println(moveSpeed);

  //If the button is pushed, move servo back to X 90, Y 90
  //This should move servo back slowly, not suddenly
  if (button == 0) {
    digitalWrite(LED_BUILTIN, HIGH);
    //resetServoPosition();
  } else {
    digitalWrite(LED_BUILTIN, LOW);
  }

  //Check joystick X Asix input

  if ( x > joystick_center + joystick_tolerance) {
    Serial.print("X_Pos + : ");
    Serial.println(x_last);
    //ensure servo is in the limited range, or it will not move

    if (! (x_last + moveSpeed > x_maxAngle)) {
      x_last += moveSpeed;
      servo1_x.write(x_last);
    } else {
      //move to the X max allowed angle
      x_last = x_maxAngle;
      servo1_x.write(x_maxAngle);
    }
  } else if (x < joystick_center - joystick_tolerance) {
    Serial.print("X_Pos - : ");
    Serial.println(x_last);
    //ensure servo is in the limited range, or it will not move
    if (! (x_last - moveSpeed < x_minAngle)) {
      x_last -= moveSpeed;
      servo1_x.write(x_last);
    } else {
      //move to the X min allowed angle
      x_last = x_minAngle;
      servo1_x.write(x_minAngle);
    }
  }

  //Check Joystick  Y Asix input
  if ( y > joystick_center + joystick_tolerance) {
    Serial.print("Y_Pos + :");
    Serial.println(y_last);
    //ensure servo is in the limited range, or it will not move
    if (! (y_last + moveSpeed > y_maxAngle)) {
      y_last += moveSpeed;
      servo2_y.write(y_last);
    } else {
      //move to the Y min allowed angle
      y_last = y_maxAngle;
      servo2_y.write(y_last);
    }
  } else if (y < joystick_center - joystick_tolerance) {
    Serial.print("Y_Pos - :");
    Serial.println(y_last);
    //ensure servo is in the limited range, or it will not move
    if (! (y_last - moveSpeed < y_minAngle)) {
      y_last -= moveSpeed;
      servo2_y.write(y_last);
    } else {
      //move to the Y min allowed angle
      y_last = y_minAngle;
      servo2_y.write(y_last);
    }
  }

  //moveSpeed from 1 ~ 48
  //So, the delay is calculated to be from  5ms ~ 240ms
  uint8_t delayTime = (480 - (moveSpeed - 1) * 10) / 2;
  //  Serial.print(F("Delay time:"));
  //  Serial.println(delayTime);
  delay(delayTime);

}

//This function moves servo back to originalAngle
void resetServoPosition() {


  //Move X
  if (x_last >= 90) {
    for (int i = x_last; i <= originalAngle; i -= 5) {
      servo1_x.write(i);
      Serial.print(i);
    }
  } else {
    for (int i = x_last;  i > originalAngle; i += 5) {
      servo1_x.write(i);

    }
  }
  Serial.print("X_last - : ");
  Serial.println(x_last);
  //Move Y
  if (y_last >= 90) {
    for (int i = y_last; i <= originalAngle; i -= 5) {
      servo2_y.write(i);

    }
  } else {
    for (int i = y_last;  i > originalAngle; i += 5) {
      servo2_y.write(i);

    }
  }

  Serial.print("Y_last - :");
  Serial.println(y_last);
}

void print_joystick_info() {
  Serial.print("button: ");
  Serial.print(digitalRead(joystick_s_pin));
  Serial.print("\n");
  Serial.print("X-axis: ");
  Serial.print(analogRead(joystick_x_pin));
  Serial.print("\n");
  Serial.print("Y-axis: ");
  Serial.println(analogRead(joystick_y_pin));
  Serial.print("\n");

}




The End.
=================================
中文版


Arduino 用搖桿控制的雙向轉軸的平台
今天的Arduino教學中,我將教你如何製作-用搖桿控制的雙向轉軸的平台
而且用可變電阻控制速度。

整個平台通過畫面右下方的搖桿進行控制,而且可以用可變電阻用來
控制SG90伺服器的移動速度。

3D 列印雙軸平台




線路示意圖:



在下方的影片裡,當我轉動可變電阻鈕時,Arduino 串列輸出視窗裡
顯示速度已增加  9, 你可以看見,隻軸平台的移動得快一些。

影片中,也會調整可變電阻來試試看不同的速度。

實際運作影片:



 所需零件: 

  • 一個 Arduino Nano
  • 一個搖桿模組
  • 一個可變電阻
  • 兩個SG90伺服器
  • 一個3D列印的雙向轉軸的平台 (謝謝好心FB網友幫忙列印!!)

連線方式:

  •  Connect joystick GND, 5V, VRX to A4, VRX to A3 and SW to D6 pins to Arduino.
  •  Connect 1st SG90 servo VCC to 5V, D2, and GND to Arduino 
  •  Connect 2nd SG90 servo VCC to 5V, D4, and GND to Arduino 
  •  Connect potentiometer VCC to 5V, A0, and GND to Arduino 

我正在考慮將這個玩具再增加一點功能....自動的光源追踪器。希望有一天能做好!
我希望你喜歡這個教學,玩得開心!

這裡還有一些我之前做過的東西:

式參考資料:


原始程式:

請參考上面:

全篇完

2017/09/16

Testing Arduino POV (Persistent of Vision) | 試試 Arduino POV (視覺暫留)

I have been attracted to Arduino POV[1] for quite a while, but I didn't have a chance to actually make it myself.   Luckily, I found this easy to follow Arduino POV tutorial[2] on Instructable.com and within an hour, my son and me made two Arduino POV sets and have fun for the entire afternoon.


Material needed for this tutorial:

  • Arduino Nano * 1 (All other kinds of Arduino board will do)
  • LED * 7
  • 220 resistor * 7 
  • Jump wire ~ up to 10 (female to male)
  • Prefboard * 1 
  • Battery pack (If you would like have fun without tethered with USB)

Easy Wire Connection as follow:

  1. LED1 ~ LED7 negative pin to Prefboard GND
  2. Prefboard to Nano(or Uno) GND pin
  3. LED1 ~ LED7 postive pin to Nano(or Uno) D2 ~ D8 pin
  4. USB power to Nano USB port to supply power (or use a battery back to supply power)

Schematic: (I use www.tinkercad.com to draw this schematic below, just in case you interested.)



This is a fun a easy project! First, just connect LED cathode(negative) and resistors on the prefboard.
Then Preboard GND to Arduino Nano GND.


Then connect these LED positive pins to Arduino Nano.





As you can see in the picture below, I'v used Arduino Nano. It's lighter and smaller for portable devices.  I made the Red one and my son have choose the Green.



Programming logic:
When anyone sees an image, the image stays in the retina of the eyes for roughly 1/16th seconds. We can utilize the particular physical characteristic of human eyes, to crate the Arduino POV project. The basic idea is when we want to show a letter, Arduino shows each column of a letter at a time to form the letter to eyes.

See the illustration below on how to show a letter 'H'. First, Arduino displays column 1 to light up all 7 LEDs at timing 1. While column 1 image stays in human eyes, Arduino shows 2nd column, then 3, 4, and 5th columns at their respectively timing 2~5. Since all these images stays in human eyes temporarily, we will see the letter 'H'.



Actual LED image captured:



Arduino code here:
Just copy and past into Arduino IDE and it should work.
*Change your own message in the variable msgBody and remember to change msgLength*

/*
 Written by Ahmad Saeed on 22 August 2015
 
 This code is capable of displaying the following characters;
 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ .-_!%&#$,()@?
*/ 

//////////////////// Message to Customize  ///////////////////
#define msgLength 11                                       ///
String msgBody = "This is POV";                            ///
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////




#define delayInChar 3
#define delayBetweenChar 5
#define LED1 2
#define LED2 3
#define LED3 4
#define LED4 5
#define LED5 6
#define LED6 7
#define LED7 8

byte msgCode[(5 * msgLength) + 10];
boolean pintState;
int columnNum = -1;
String charToWrite;

void setup() {
  msgBody.toUpperCase();
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  pinMode(LED5, OUTPUT);
  pinMode(LED6, OUTPUT);
  pinMode(LED7, OUTPUT);
}

void loop() {
//// Convert all text to binary array ////////////////////////
  if ( columnNum == -1 ) // This block needs to be done once//
  {                                                         //
    for (int c = 0; c < (msgBody.length()); c++)  {         //
      //Separate the following character                    //
      charToWrite = msgBody.substring(c, c + 1);            //
      //Send the separated characted to addChar function    //
      addChar(charToWrite);                                 //
    }                                                       //
      //Add a little space after each character             //
    addChar(" ");                                           //
    addChar(" ");                                           //
  }                                                         //  
//////////////////////////////////////////////////////////////

//// Display the binary arrays after all characters are coded //
  for (int c = 0; c < (sizeof(msgCode)); c++)  {              //
    pintState = (msgCode[c] / B1000000) % B10;                //
    digitalWrite(LED1, pintState);                            //
                                                              //
    pintState = (msgCode[c] / B100000) % B10;                 //
    digitalWrite(LED2, pintState);                            //
                                                              //
    pintState = (msgCode[c] / B10000) % B10;                  //
    digitalWrite(LED3, pintState);                            //
                                                              //
    pintState = (msgCode[c] / B1000) % B10;                   //
    digitalWrite(LED4, pintState);                            //
                                                              //
    pintState = (msgCode[c] / B100) % B10;                    //
    digitalWrite(LED5, pintState);                            //
                                                              //
    pintState = (msgCode[c] / B10) % B10;                     //
    digitalWrite(LED6, pintState);                            // 
                                                              //
    pintState = msgCode[c] % B10;;                            //
    digitalWrite(LED7, pintState);                            //
                                                              //
    delay(delayInChar);                                       //
    // if the character is finished, take a longer off period //
    if ((c + 1) % 5 == 0 ) {                                  //
      digitalWrite(LED1, LOW);                                //
      digitalWrite(LED2, LOW);                                //
      digitalWrite(LED3, LOW);                                //
      digitalWrite(LED4, LOW);                                //
      digitalWrite(LED5, LOW);                                // 
      digitalWrite(LED6, LOW);                                //
      digitalWrite(LED7, LOW);                                //
      delay(delayBetweenChar);                                //
    }                                                         //
  }                                                           //
                                                              //
////////////////////////////////////////////////////////////////
}     


void addChar(String y) {
  if (y == "1") {
    addColumn(B0010001);
    addColumn(B0100001);
    addColumn(B1111111);
    addColumn(B0000001);
    addColumn(B0000001);
  }
  else if (y == "2") {
    addColumn(B0100001);
    addColumn(B1000011);
    addColumn(B1000101);
    addColumn(B1001001);
    addColumn(B0110001);
  }
  else if (y == "3") {
    addColumn(B0100010);
    addColumn(B1000001);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B0110110);
  }
  else if (y == "4") {
    addColumn(B0001100);
    addColumn(B0010100);
    addColumn(B0100100);
    addColumn(B1111111);
    addColumn(B0000100);
  }
  else if (y == "5") {
    addColumn(B1110010);
    addColumn(B1010001);
    addColumn(B1010001);
    addColumn(B1010001);
    addColumn(B1001110);
  }
  else if (y == "6") {
    addColumn(B0111110);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B0100110);
  }
  else if (y == "7") {
    addColumn(B1000000);
    addColumn(B1000111);
    addColumn(B1001000);
    addColumn(B1010000);
    addColumn(B1100000);
  }
  else if (y == "8") {
    addColumn(B0110110);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B0110110);
  }
  else if (y == "9") {
    addColumn(B0110010);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B0111110);
  }
  else if (y == "0") {
    addColumn(B0111110);
    addColumn(B1000101);
    addColumn(B1001001);
    addColumn(B1010001);
    addColumn(B0111110);
  }
  else if (y == "A") {
    addColumn(B0011111);
    addColumn(B0100100);
    addColumn(B1000100);
    addColumn(B1000100);
    addColumn(B1111111);
  }
  else if (y == "B") {
    addColumn(B1111111);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B0110110);
  }
  else if (y == "C") {
    addColumn(B0111110);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B0100010);
  }
  else if (y == "D") {
    addColumn(B1111111);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B0111110);
  }
  else if (y == "E") {
    addColumn(B1111111);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B1000001);
  }
  else if (y == "F") {
    addColumn(B1111111);
    addColumn(B1001000);
    addColumn(B1001000);
    addColumn(B1001000);
    addColumn(B1000000);
  }
  else if (y == "G") {
    addColumn(B0111110);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B1000101);
    addColumn(B0100110);
  }
  else if (y == "H") {
    addColumn(B1111111);
    addColumn(B0001000);
    addColumn(B0001000);
    addColumn(B0001000);
    addColumn(B1111111);
  }
  else if (y == "I") {
    addColumn(B0000000);
    addColumn(B1000001);
    addColumn(B1111111);
    addColumn(B1000001);
    addColumn(B0000000);
  }
  else if (y == "J") {
    addColumn(B0000000);
    addColumn(B0000010);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B1111110);
  }
  else if (y == "K") {
    addColumn(B1111111);
    addColumn(B0001000);
    addColumn(B0010100);
    addColumn(B0100010);
    addColumn(B1000001);
  }
  else if (y == "L") {
    addColumn(B1111111);
    addColumn(B0000001);
    addColumn(B0000001);
    addColumn(B0000001);
    addColumn(B0000001);
  }
  else if (y == "M") {
    addColumn(B1111111);
    addColumn(B0100000);
    addColumn(B0011000);
    addColumn(B0100000);
    addColumn(B1111111);
  }
  else if (y == "N") {
    addColumn(B1111111);
    addColumn(B0010000);
    addColumn(B0001000);
    addColumn(B0000100);
    addColumn(B1111111);
  }
  else if (y == "O") {
    addColumn(B0111110);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B1000001);
    addColumn(B0111110);
  }
  else if (y == "P") {
    addColumn(B1111111);
    addColumn(B1001000);
    addColumn(B1001000);
    addColumn(B1001000);
    addColumn(B0110000);
  }
  else if (y == "Q") {
    addColumn(B0111100);
    addColumn(B1000010);
    addColumn(B1000010);
    addColumn(B1000010);
    addColumn(B0111101);
  }
  else if (y == "R") {
    addColumn(B1111111);
    addColumn(B1001000);
    addColumn(B1001100);
    addColumn(B1001010);
    addColumn(B0110001);
  }
  else if (y == "S") {
    addColumn(B0110010);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B1001001);
    addColumn(B0100110);
  }
  else if (y == "T") {
    addColumn(B1000000);
    addColumn(B1000000);
    addColumn(B1111111);
    addColumn(B1000000);
    addColumn(B1000000);
  }
  else if (y == "U") {
    addColumn(B1111110);
    addColumn(B0000001);
    addColumn(B0000001);
    addColumn(B0000001);
    addColumn(B1111110);
  }
  else if (y == "V") {
    addColumn(B1111100);
    addColumn(B0000010);
    addColumn(B0000001);
    addColumn(B0000010);
    addColumn(B1111100);
  }
  else if (y == "W") {
    addColumn(B1111110);
    addColumn(B0000001);
    addColumn(B0000110);
    addColumn(B0000001);
    addColumn(B1111110);
  }
  else if (y == "X") {
    addColumn(B1100011);
    addColumn(B0010100);
    addColumn(B0001000);
    addColumn(B0010100);
    addColumn(B1100011);
  }
  else if (y == "Y") {
    addColumn(B1110000);
    addColumn(B0001000);
    addColumn(B0001111);
    addColumn(B0001000);
    addColumn(B1110000);
  }
  else if (y == "Z") {
    addColumn(B1000011);
    addColumn(B1000101);
    addColumn(B1001001);
    addColumn(B1010001);
    addColumn(B1000011);
  }
  else if (y == "Z") {
    addColumn(B1000011);
    addColumn(B1000101);
    addColumn(B1001001);
    addColumn(B1010001);
    addColumn(B1000011);
  }
  else if (y == " ") {
    addColumn(B0000000);
    addColumn(B0000000);
    addColumn(B0000000);
    addColumn(B0000000);
    addColumn(B0000000);
  }
  else if (y == ".") {
    addColumn(B0000000);
    addColumn(B0000011);
    addColumn(B0000011);
    addColumn(B0000000);
    addColumn(B0000000);
  }
  else if (y == "_") {
    addColumn(B0000001);
    addColumn(B0000001);
    addColumn(B0000001);
    addColumn(B0000001);
    addColumn(B0000001);
  }
  else if (y == "-") {
    addColumn(B0000000);
    addColumn(B0001000);
    addColumn(B0001000);
    addColumn(B0001000);
    addColumn(B0000000);
  }
  else if (y == "!") {
    addColumn(B0000000);
    addColumn(B0000000);
    addColumn(B1111101);
    addColumn(B0000000);
    addColumn(B0000000);
  }
  else if (y == "(") {
    addColumn(B0000000);
    addColumn(B0000000);
    addColumn(B0000000);
    addColumn(B0111110);
    addColumn(B1000001);
  }
  else if (y == ")") {
    addColumn(B1000001);
    addColumn(B0111110);
    addColumn(B0000000);
    addColumn(B0000000);
    addColumn(B0000000);
  }
  else if (y == "%") {
    addColumn(B1100010);
    addColumn(B1100100);
    addColumn(B0001000);
    addColumn(B0010011);
    addColumn(B0100011);
  }
  else if (y == ",") {
    addColumn(B0000000);
    addColumn(B0000101);
    addColumn(B0000110);
    addColumn(B0000000);
    addColumn(B0000000);
  }
  else if (y == "?") {
    addColumn(B0100000);
    addColumn(B1000101);
    addColumn(B1001000);
    addColumn(B0110000);
    addColumn(B0000000);
  }
  else if (y == "#") {
    addColumn(B0010100);
    addColumn(B0111110);
    addColumn(B0010100);
    addColumn(B0111110);
    addColumn(B0010100);
  }
  else if (y == "@") {
    addColumn(B0111110);
    addColumn(B1000001);
    addColumn(B1011101);
    addColumn(B1011101);
    addColumn(B0111000);
  }
  else if (y == "$") {
    addColumn(B0110010);
    addColumn(B1001001);
    addColumn(B1111111);
    addColumn(B1001001);
    addColumn(B0100110);
  }
}

void addColumn(byte x) {
  columnNum += 1;
  msgCode[columnNum] = (x);
}



Image captured with my POV set:




Image captured with my son's POV set:


How to capture the image:
I used HTC U11 to shot these photos. First, you have change HTC camera to Pro mode and set the exposure time to greater than 2 seconds to capture this image. Hold the Arduino POV set steadily on one hand and move the POV set slowly towards to left from right.  I believe any camera with Bulb exposure time greater than 2 seconds should be able to capture this image. 

That's all for this tutorial. See you next time!

Article feferences:
  1. Wikipedia for POV
  2. How to Make a POV Display Using LEDs and Arduino 
  3. Simple-Arduino-POV-Wand 


Done.

==== 中 文 版 本 ====

自從我看個幾個 Arduino POV[1] 專案之後,一直想自己試試做一個來玩玩,但一直有點懶惰,遲遲沒有動手。最近看到一篇很簡單的教學 Arduino POV tutorial[2] ,剛好兒子也在家,我們決定一起動手來做一個來玩。只需約一小時,我們就各自做了一組 Arduino POV,而且整個下午都在試著不同的字串及拿著 HTC U11 拍出很一些有趣的照片。這是一個很好的親子活動的安排哩!現在就看著這篇教學一起動手吧!


這篇教學所需材料:

  • Arduino Nano * 1 (Nano, Pro mini, Uno 都可以)
  • LED * 7
  • 220 歐姆電阻 * 7 
  • 杜邦線 ~ 約 10條 (母對母)
  • 洞洞板 * 1 
  • 電池盒 (不用連接USB,可以更方便的玩,但可有可無)

接線方式:
  1. LED1 ~ LED7 負級 to 220 歐姆電阻
  2. 電阻 to Nano(or Uno) GND
  3. LED1 ~ LED7 正級 to Nano(or Uno) D2 ~ D8 腳位
  4. USB 電源接 Nano USB 來提供電源 (這可以用電池盒來取代)

電路圖: (我是用 www.tinkercad.com 來製作這張電路圖)



這真的是一個很簡單又好玩的範例! 
道先, 先把 LED 負級連到220歐姆的電阻再由電阻連接到洞洞板的負極上。


接著,把LED 1 ~ 7 的正極接到 Arduino 上的 D2 ~ D8 腳位上。





我這次是使用了 Arduino Nano,因為它的體積比較小。下圖中可以看到我們做了兩組,我做了紅色的這組,我兒子則做了綠色的這一組。



程式的邏輯如下:
原理是當我們看到一個影像時,這個影像會暫留在視網膜上約 1/16 秒。當影像一個接著一個的顯示後,我們即會看到好像是數個影像的重疊,這些重疊的影像看起來就會形成一幅影像;卡通或動畫即是這樣的原理。Arduino POV 就是利用同樣的原理。

看下例如何顯示 H 字母。首先,Arduino 在 Time 1 顯示了欄位 1 的 7 個 LED,然後 Arduino 在 Time 2 ~ 5 個別的顯示欄位 2 ~ 5。 由於每個欄位都會在視網膜上暫留,我們就可以在到字母 'H'。


你可以參考這幾個字母放大後的影像如下:



Arduino 程式碼在上方,直接拷貝就可以使用:

*如果你要改變顯示的字串,請記得改變 msgBody 字串以及 msgLength 的字串長度*





下方是我用 HTC U11 照出來的 POV 照片:




這張是用我兒子那一組所拍下來的POV照片


如何照下 Arduino POV 的照片:
我是使用 HTC U11 來拍攝這些照片的。首先,你必需把手機設定到 PRO 模式 ,然後選定曝光時間到 2 秒以上才能拍的出來。拍攝時,拿著洞洞板,把 LED 朝相機方向,慢慢的由左到右的移動。不過,任何可以選定手動曝光到 2 秒以上的相機應該都可以拍的。

這就是這次的Arduino 範例,下次見!

文章參考:
  1. Wikipedia for POV
  2. How to Make a POV Display Using LEDs and Arduino 
  3. Simple-Arduino-POV-Wand 

2017/09/03

Test PIR Sensor with Arduino | PIR 感應器

In this tutorial, I would like to show you how to make a PIR sensor to work with Arduino.  PIR stands for Passive Infrared sensor. What it does is to measure infrared (IR) light radiating from objects in its field of view.

A frequently use case is installing a PIR sensor at the hall way and whenever PIR sensor detects people walk towards the hall way, then the PIR sensor turn on the light.   Please refer to Wikipedia for more information about PIR.

Material needed for this tutorial:

  • Arduino Nano * 1 (All other kinds of Arduino board will do)
  • PIR sensor * 1
  • Buzzer * 1
  • LED * 1 (You may just utilize the LED on Arduino Nano)
  • 220 resistor * 1 (No need you use the LED on Arduino Nano)
  • Jump wire ~ up to 10 (female to female)

Wire Connection as follow:

  1. PIR Ground pin to Nano(or Uno) GND pin
  2. PIR Power pin to Nano +5V pin
  3. PIR data pin to Nano D2 pin
  4. LED ground to Nano GND pin
  5. LED power pin to 220K resistor pin
  6. 220K resistor pin to Nano pin 13
  7. Buzzer postive pin to Nano D8 pin
  8. Buzzer ground pin to Nano GND pin

Schematic: (I use www.tinkercad.com to draw this schematic below, just in case you interested.)




Programming logic:
Once the PIR sensor detects objects entered to its detection range, light up LED and sound the Buzzer for one second.

Arduino code here:

#define NOTE_C5  523
const int PIRSensor = 2;
const int ledPin =  13;
int sensorValue = 0;
void setup() {
  pinMode(PIRSensor, INPUT);
  pinMode(ledPin, OUTPUT);
}
void loop() {
sensorValue = digitalRead(PIRSensor);
if (sensorValue == HIGH) { 
    // Object detected, turn on LED, sound the buzzer for 1 second
    tone(8, NOTE_C5);
    digitalWrite(ledPin, HIGH);
    delay(1000);
}else{
    // No object detected, turn off LED, mute the buzzer
    noTone(8);
    digitalWrite(ledPin, LOW);
}



See the demo on Youtube:





Done.
==== 中 文 版 本 ==========================================
這次的教學, 我想試試看 Arduino 的 PIR 感測器.  PIR 是被動式感應器的縮寫. 它可以偵測在它的視線內從物體發射出的紅外線.

最常使用到 PIR 感測器 的地方就是不常有人經過的地方,當有人經過時,PIR 感測器感應到紅外線就會把燈打開。 請參考維基百科的資訊。

這次教學所需的材料:
  • Arduino Nano * 1 (其他 Arduino 板子也可以)
  • PIR 偵測器 * 1
  • 蜂鳴器 * 1
  • LED * 1 (也可以使用 Arduino Nano 上的內建 LED)
  • 220 電阻 * 1 (若使用 Arduino Nano 上的內建 LED就不需要)
  • 杜邦線 大約十個 (母對母)

連線方式:

  1. PIR Ground pin to Nano(or Uno) GND pin
  2. PIR Power pin to Nano +5V pin
  3. PIR data pin to Nano D2 pin
  4. LED ground to Nano GND pin
  5. LED power pin to 220K resistor pin
  6. 220K resistor pin to Nano pin 13
  7. Buzzer postive pin to Nano D8 pin
  8. Buzzer ground pin to Nano GND pin

線路圖: (我是使用 www.tinkercad.com 來會畫這張線路圖, 如果你有興趣可以參考一下)




程式邏輯:
很簡單,當 PIR 感測器在他的視線內感應到紅外線, 將 LED 和蜂鳴器開啟一秒鐘.

Arduino 程式碼在這:

#define NOTE_C5  523
const int PIRSensor = 2;
const int ledPin =  13;
int sensorValue = 0;
void setup() {
  pinMode(PIRSensor, INPUT);
  pinMode(ledPin, OUTPUT);
}
void loop() {
sensorValue = digitalRead(PIRSensor);
if (sensorValue == HIGH) { 
    // Object detected, turn on LED, sound the buzzer for 1 second
    tone(8, NOTE_C5);
    digitalWrite(ledPin, HIGH);
    delay(1000);
}else{
    // No object detected, turn off LED, mute the buzzer
    noTone(8);
    digitalWrite(ledPin, LOW);
}




看看Youtube上的示範: