2015年2月27日 星期五

修改S4A韌體

前一節修改S4A腳位定義,也修改S4A韌體中對應的腳位,但Arduino感測器種類繁多,不是都修改腳位就可使用,部分感測器需加入特定函式庫這些都要在S4A韌體中自行撰寫。

本篇以超音波為例:
超音波感測器(老外好像把它叫作 PING))) sensor)是由超音波發射器、接收器和控制電路所組成。當它被觸發的時候,會發射一連串 40 kHz 的聲波並且從離它最近的物體接收回音。超音波是人類耳朵無法聽見的聲音,因為它的頻率很高。





加入到Arduino的libraries中



// NEW IN VERSION 1.5:
// Changed pin 8 from standard servo to normal digital output

// NEW IN VERSION 1.4:
// Changed Serial.print() for Serial.write() in ScratchBoardSensorReport function to make it compatible with latest Arduino IDE (1.0)

// NEW IN VERSION 1.3:
// Now it works on GNU/Linux. Also tested with MacOS and Windows 7.
// timer2 set to 20ms, fixing a glitch that made this period unstable in previous versions.
// readSerialport() function optimized.
// pulse() modified so that it receives pulse width as a parameter instead using a global variable.
// updateServoMotors changes its name as a global variable had the same name.
// Some minor fixes.

// Thanks to Jorge Gomez for all these new fixes!
#include <Ultrasonic.h>// 超音函式褲
#define TIMER2_PRELOAD 100

#define TRIGGER_PIN  5 //觸發超音波
#define ECHO_PIN     A5 //傳回偵測值
Ultrasonic ultrasonic(TRIGGER_PIN, ECHO_PIN);

char outputs[10];
int states[10];

unsigned long initialPulseTime;
unsigned long lastDataReceivedTime;

volatile boolean updateServoMotors;
volatile boolean newInterruption;

void setup()
{
  Serial.begin(38400);
  Serial.flush();
  configurePins();
  configureServomotors();
  lastDataReceivedTime = millis();
}

void loop()
{
  if (updateServoMotors)
  {
    sendUpdateServomotors();
    sendSensorValues();
    updateServoMotors = false;
  }
  else
  {
    readSerialPort();
  }
}

void configurePins()
{
  for (int index = 0; index < 10; index++)
  {
    states[index] = 0;
    pinMode(index+4, OUTPUT);
    digitalWrite(index+4, LOW); //reset pins
  }

  pinMode(2,INPUT);
  pinMode(3,INPUT);

  outputs[0] = 'c'; //pin 4
  outputs[1] = 'a'; //pin 5
  outputs[2] = 'a'; //pin 6
  outputs[3] = 'c'; //pin 7
  outputs[4] = 's'; //pin 8
  outputs[5] = 'a'; //pin 9
  outputs[6] = 'd'; //pin 10
  outputs[7] = 'd'; //pin 11
  outputs[8] = 'd'; //pin 12
  outputs[9] = 'd'; //pin 13
}

void configureServomotors() //servomotors interruption configuration (interruption each 10 ms on timer2)
{
  newInterruption = false;
  updateServoMotors = false;

  TCCR2A = 0;
  TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;
  TIMSK2 = 1<<TOIE2; //timer2 Overflow Interrupt
  TCNT2 = TIMER2_PRELOAD; //start timer
}

void sendSensorValues()
{
  int sensorValues[6], readings[5], sensorIndex;
  for (sensorIndex = 0; sensorIndex < 5; sensorIndex++)  // 將原來的 6 改為 5
  {
    for (int p = 0; p < 5; p++)
      readings[p] = analogRead(sensorIndex);
    InsertionSort(readings, 5); //sort readings
    sensorValues[sensorIndex] = readings[2]; //select median reading
  }

  //send analog sensor values
  for (sensorIndex = 0; sensorIndex < 5; sensorIndex++)  // 將原來的 6 改為 5
    ScratchBoardSensorReport(sensorIndex, sensorValues[sensorIndex]);

  // 自已定義的程序
  ReadExtra();  // 讀取超音波

  //send digital sensor values
  ScratchBoardSensorReport(6, digitalRead(2)?1023:0);
  ScratchBoardSensorReport(7, digitalRead(3)?1023:0);
}

void ReadExtra()  // 自已定義的程序,讀取超音波
{   
  long microsec = ultrasonic.timing();  // 取得傳回時間
  float cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); // 計算距離,單位: 公分
  ScratchBoardSensorReport(5, cmMsec);  // 將讀取的類比角位傳回 Arduino
}

void InsertionSort(int* array, int n)
{
  for (int i = 1; i < n; i++)
    for (int j = i; (j > 0) && ( array[j] < array[j-1] ); j--)
      swap( array, j, j-1 );
}

void swap (int* array, int a, int b)
{
  int temp = array[a];
  array[a] = array[b];
  array[b] = temp;
}

void ScratchBoardSensorReport(int sensor, int value) //PicoBoard protocol, 2 bytes per sensor
{
  Serial.write( B10000000
    | ((sensor & B1111)<<3)
    | ((value>>7) & B111));
  Serial.write( value & B1111111);
}

void readSerialPort()
{
  int pin, inByte, sensorHighByte;

  if (Serial.available() > 1)
  {
    lastDataReceivedTime = millis();
    inByte = Serial.read();

    if (inByte >= 128) // Are we receiving the word's header?
    {
      sensorHighByte = inByte;
      pin = ((inByte >> 3) & 0x0F);
      while (!Serial.available()); // Wait for the end of the word with data
      inByte = Serial.read();
      if (inByte <= 127) // This prevents Linux ttyACM driver to fail
      {
        states[pin - 4] = ((sensorHighByte & 0x07) << 7) | (inByte & 0x7F);
        updateActuator(pin - 4);
      }
    }
  }
  else checkScratchDisconnection();
}

void reset() //with xbee module, we need to simulate the setup execution that occurs when a usb connection is opened or closed without this module
{
  for (int pos = 0; pos < 10; pos++)  //stop all actuators
  {
    states[pos] = 0;
    digitalWrite(pos + 2, LOW);
  }

  //reset servomotors
  newInterruption = false;
  updateServoMotors = false;
  TCNT2 = TIMER2_PRELOAD;

  //protocol handshaking
  sendSensorValues();
  lastDataReceivedTime = millis();
}

void updateActuator(int pinNumber)
{
  if (outputs[pinNumber] == 'd')  digitalWrite(pinNumber + 4, states[pinNumber]);
  else if (outputs[pinNumber] == 'a')  analogWrite(pinNumber + 4, states[pinNumber]);
}

void sendUpdateServomotors()
{
  for (int p = 0; p < 10; p++)
  {
    if (outputs[p] == 'c') servomotorC(p + 4, states[p]);
    if (outputs[p] == 's') servomotorS(p + 4, states[p]);
  }
}

void servomotorC (int pinNumber, int dir)
{
  if (dir == 1) pulse(pinNumber, 1300); //clockwise rotation
  else if (dir == 2) pulse(pinNumber, 1700); //anticlockwise rotation
}

void servomotorS (int pinNumber, int angle)
{
  if (angle < 0) pulse(pinNumber, 600);
  else if (angle > 180) pulse(pinNumber, 2400);
  else pulse(pinNumber, (angle * 10) + 600);
}

void pulse (int pinNumber, int pulseWidth)
{
  initialPulseTime = micros();
  digitalWrite(pinNumber, HIGH);

  while (micros() < pulseWidth + initialPulseTime){
  }
  digitalWrite(pinNumber, LOW);
}

void checkScratchDisconnection() //the reset is necessary when using an wireless arduino board (because we need to ensure that arduino isn't waiting the actuators state from Scratch) or when scratch isn't sending information (because is how serial port close is detected)
{
  if (millis() - lastDataReceivedTime > 1000) reset(); //reset state if actuators reception timeout = one second
}

ISR(TIMER2_OVF_vect) //timer1 overflow interrupt vector handler
{ //timer2 => 8 bits counter => 256 clock ticks
  //preeescaler = 1024 => this routine is called 61 (16.000.000/256/1024) times per second approximately => interruption period =  1 / 16.000.000/256/1024 = 16,384 ms
  //as we need a 20 ms interruption period but timer2 doesn't have a suitable preescaler for this, we program the timer with a 10 ms interruption period and we consider an interruption every 2 times this routine is called.
  //to have a 10 ms interruption period, timer2 counter must overflow after 156 clock ticks => interruption period = 1 / 16.000.000/156/1024 = 9,984 ms => counter initial value (TCNT) = 100
  if (newInterruption)
  {
    updateServoMotors = true;
  }
  newInterruption = !newInterruption;
  TCNT2 = TIMER2_PRELOAD;  //reset timer
}

2015年2月7日 星期六

APP控制S4A應用程式

範例:電流急急棒

1.新建立的檔案無法取得虛擬感測器積木,必須先用控制S4A的APP傳送虛擬感測器數值才能產生虛擬感測積木。

各種音效





APP的部分我以Inventor2開發,其中ballX跟ballY要同時傳送所以傳送虛擬感測器給S4A語法要加&連結。
EX:http://IP位址:42001/?sensor-update=ballY=感測值&sensor-update=ballX=感測值