2019年7月1日 星期一

如何自製arduino的函式庫

動機是來自Otto。

因為自己組合了有手的Otto,但是APP卻都是只有腳的部分,所以想說自己編寫APP看看,發現自己要重複編寫那些動作很麻煩,而library的函式庫就是在處理這種重複功能用的,就想來研究看看。

在看過幾本書幾篇文章之後,嗯,完全看不懂,果然代誌不是憨人想得那麼簡單,於是又來記錄自己的學習過程。

我是先參考底下這一篇。

https://chtseng.wordpress.com/2017/05/31/%E5%A6%82%E4%BD%95%E7%B7%A8%E5%AF%ABarduino-library/

釐清自己對於library的觀念就是會不斷使用的程式碼寫成函式,就可以只呼叫函式名稱來使用,不用再重複整段程式碼。

所以,首先就是確認自己要改成library的程式碼可以用,再以既定的格式改編。

依照格式來說要準備三個檔案,
函式名稱.h                   (這是定義函式功能的變數、參數,稱為header file)
函式名稱.cpp               (這是C++編譯使用的檔案,執行的動作放這邊,稱為source file)
keywords.txt                 (這是在編寫程式有寫到關鍵字時,要變色,檔案可有可無)

這時候發現一開始看不懂的問題就是,不知道該把什麼放到哪裡,因為作者都很熟了,所以文章的內容就很順理成章的把程式碼都已經分類到兩個檔案去了,但是我卻完全搞不清楚為什麼要這樣分類。

就走一步算一步了。

在上面連結有一個例子,說明如何將閃爍的功能寫成library。
這時候已經稍有概念了,但是還似懂非懂,看了幾篇還是有疑問,最後想說找arduino官網的說明看看,底下是說明摩斯密碼的例子,看起來比較基礎的樣子,我先紀錄這個功能試試看之後,再回頭看其他的。

https://www.arduino.cc/en/Hacking/LibraryTutorial

摩斯密碼適用亮光的時間長跟短來組合出文字,底下就是長跟短的程式碼

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

再根據要幾個長跟幾個短來呼叫就可以

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

完整的程式碼

int pin = 13;

void setup()
{
  pinMode(pin, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

確認這段程式碼的動作沒有問題,接下來就是開始拆解。
以功能來說,就是長dash跟短dot,還有一個變數,就是腳位。

我們先在header file定義功能跟變數,使用的是class,private是呼叫Morse時才能使用, public則是整段程式碼都可以用,基本上,盡量使用private,減少公用記憶體的用量

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

不過,我們自己定義的功能根本上來說,是使用arduino的基本功能,所以我們需要把arduino的功能放進去。

#include "Arduino.h"

最後是檔案頭尾要放上宣告header file用的字眼。

#ifndef Morse_h
#define Morse_h

// the #include statment and code go here...

#endif


所以完整的header file會是
#ifndef Morse_h
#define Morse_h

#include "Arduino.h"

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

接著是source file,先把變數的作用寫出來,就是指定腳位並設定為output。

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

接著是長跟短的時間
void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

有一個雙冒號的記號::,那是指附屬功能,比如void Morse::dot()就是指附屬在Morse的dot功能,用這個就可以描述變數跟功能。

其實,我還有一個地方不懂,既然腳位已經用pin變數來指定了,為什麼還要再加一個_pin,而且還設定_pin=pin,用兩個變數來代表同一個意思。

雖然source file不需要頭尾宣告,但是,還是要把需要的功能寫進來,

#include "Arduino.h"
#include "Morse.h"

因此,完整的source file的內容就會是

#include "Arduino.h"
#include "Morse.h"

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

有了完整的header file 跟source file,就可以引用來寫程式了。
#include <Morse.h>

Morse morse(13);

void setup()
{
}

void loop()
{
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}


看來要會拆解程式,才能夠學好library,上面的例子是給了一個變數,腳位用的,
我找到一個例子,是給三個變數的,如果可以看得懂,應該可以說入門了吧。
http://yehnan.blogspot.com/2013/01/arduino.html









2019年6月27日 星期四

CC41-a不能連結Android8.0以後的版本

三年多前,淘了好幾片藍芽模組之後一直沒用,最近要安裝Otto就拿出來使用,結果發現不能連線,用模組名稱狗了一下找到底下一位法國人的文章才發現,有問題啊!

https://forum.arduino.cc/index.php?topic=393655.msg2709528




CC41-a是HM10的複製簡化版,使用的是TI的CC2541晶片,跟Android8.0之後的版本會有衝突,如果想知道HM10的資料,可以到底下的公司網站。

http://www.jnhuamao.cn/download_rom_en.asp?id=66#


雖然最上面的連結已經有教怎麼刷韌體,但是,我還是有點不太懂,在不斷搜索之下,終於發現底下的影片,也發現自己焊接技術太爛,根本是pin腳沒焊好。

https://www.youtube.com/watch?v=ez3491-v8Og

所以就去淘了底座,整個給焊上去就不怕了吧。



雖然刷好韌體了,藍牙模組的名稱也從CC41-a改成HMsoft,但是手機搜尋到之後,輸入0000也是不能連啊,查到底下網站有HM10的規格,原來PIN要輸入000000才可以。

http://www.martyncurrey.com/hm-10-bluetooth-4ble-modules/

想說用arduino連進去試試看,輸入AT是會回應OK啦,但是其他指令卻沒有任何反應。

最後,才發現HM10的指令不一樣,懶惰的我不想再去研究了,就直接使用底下路徑的方法,
http://www.martyncurrey.com/hm-10-bluetooth-4ble-modules/#HM-10%20-%20AT%20commands

終於將CC41-a給刷成HMsoft,也可以設定名稱跟密碼連線了。

2019年6月25日 星期二

紅外線

我買的接收器是VS1838B

https://github.com/z3t0/Arduino-IRremote

想要做到把接收到的遙控器編碼顯示在LCD上,並且可以控制繼電器

依照底下程式碼把線接好,使用的是內建的library

#include <LiquidCrystal.h>
#include <IRremote.h>
#include <IRremoteInt.h>

LiquidCrystal lcd(11,12,6,5,4,3);              //設定LCD需要的腳位
  int irReceiverPin = 2;                             //設定紅外線的接收腳位
   int power=7;
  IRrecv irrecv(irReceiverPin);                // 宣告一個變數儲存接收紅外線訊號
  decode_results results;                          // 宣告一個變數儲存儲存訊號解譯的結果

void setup() {
  // put your setup code here, to run once:
lcd.begin(16,2);
lcd.print("hello,world!");
Serial.begin(9600);
  irrecv.blink13(true); // 設為true的話,當收到訊號時,腳位13的LED便會閃爍
  irrecv.enableIRIn(); // 啟動接收
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.backlight();
  pinMode(7,OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
lcd.setCursor(0,1);
  if (irrecv.decode(&results)) {                  // 假如有收到紅外線訊號
    lcd.clear();
    Serial.print("results value is ");             // 輸出解碼後的資料
    Serial.println(results.value, HEX);
    lcd.print(results.value, HEX);

      switch (results.value) {
            case ;
                 //digitalWrite(7,HIGHT);
                  break;

            case ;
                   //digitalWrite(7,LOW);
                     break;   
           }
    delay(1000);
    irrecv.resume();                                     // 準備接收下一個訊號
}



但是,用上面的libraries卻一直無法正確編譯,應該是說對於同一個紅外線按鈕解譯後的數值不固定,所以去找了一本"Arduino實作入門與應用",用作者所提供的libraies,才成功抓到固定的數值,這樣,我就可以把腳位13連到繼電器,用紅外線做了一個遙控開關,


#include <LiquidCrystal.h>
#include<rc95a.h>

int cir=7;                         //紅外線腳位
int led=13;                       //led腳位
LiquidCrystal lcd(11,12,6,5,4,3);              //LCD需要的腳位
int c,i,code;

void setup() {
lcd.begin(16,2);
lcd.print("hello,world!");
pinMode(cir,INPUT);
Serial.begin(9600);
}


void loop() {
    while(1)
      {
        lcd.setCursor(0,1);
     
        Serial.println("ir code is : ");
         loop:
          no_ir=1; ir_ins(cir); if(no_ir==1) goto loop;
         rev();
          for(i=0; i<4; i++)
           {c=(int)com[i]; Serial.print(c);lcd.print(c);}

           code=(int)com[3];
           switch (code)
           {
             case 243:
                digitalWrite(led, HIGH);
                break;
             case 233:
                digitalWrite(led, LOW);
                break;
           }
            delay(30);
       }

}

2019年6月24日 星期一

想要arduino跟手機連線,監測太陽能發電

因為在南投竹山老家安裝了兩片50W的太陽能板,就一直對發電狀況很有興趣
   
   
   
但是, 當初買的控制器沒有任何查詢功能,
不過手邊有一堆淘來的arduino,所以就想說來試試看
目的很簡單,就是想看看幾點開始發電,電壓多少,
電壓很快,只要加個偵測模組就好,
幾點發電就比較麻煩,因為arduino先天記憶體的限制,
好像幾天之後就必須reset,不然就會有記憶體溢位的問題,
所以就變成反推的方式,從太陽能可以發電就開始計時,
看時間就可以反推幾點開始發電,大概知道就好,
雖然想說底下就是完成品了,





























但是,從太陽能可以發電就開始計時的話,arduino的電力就必須由太陽能供電,
所以就再找了降壓模組接到arduino,下圖就是完成品了,
其實,手癢接了個藍芽模組,就是標題說的手機連線,
結果,當我把電壓、溫度、濕度傳回手機的時候,
三個數值會在不同的欄位跳來跳去,電壓顯示在溫度,溫度顯示在溼度,
可是我明明是寫成一次傳回一個數值塞進一個欄位,再進行下一個傳送,
一直想不到問題原因,也不抓畫面了。




























另外,接太陽能的蓄電池,除了汽車用的電池之後,我又用三顆18650當成一組,
做成三組接上去充電,已經一個月是沒有問題,再觀察看看。







2019年6月17日 星期一

HC-SR04超音波測距模組


利用超音波的發射、反射、接收所需的時間來計算距離,
所使用的原理跟公式網路上的說明非常多,但是對物理系的我來說公式都看得懂,
兜到程式上就是連結不起來,只好一步一步來了。

















先講公式的原理,擷取自   https://atceiling.blogspot.com/2017/03/arduino_28.html

在攝氏零度之海平面音速約為331.5公尺/秒,每升高1攝氏度,音速就增加0.607公尺/秒,可以列出一個公式:

•  音速 c = 331.5 + 0.607 * t  (其中 t 為攝氏溫度)。

    例: 攝氏20度時的音速約為: 331.5 + 0.607*20 = 343.64 公尺/秒

因為我們是要把時間轉換成距離,所以知道速度之後,再把

•  音速公尺/秒 換算成 公分/微秒:343.64 * 100 / 1000000 = 0.034364 公分/微秒,亦即

    音速每公分需要29.1 微秒:  1 / 0.034364 = 29.1 微秒/公分

不過,最前面有說過,這個模組的作用是發射、反射、接收所需的時間,因此實際上時間是兩倍,所以還要先把時間除以二。
  duration = pulseIn(echoPin, HIGH);   // 收到高電位時的時間
 
  cm = (duration/2) / 29.1;         // 將時間換算成距離 cm   

那,這個模組是怎麼計算出這個時間呢,擷取自
 http://ming-shian.blogspot.com/2013/09/arduino-hc-sr04.html

還是脫離不了最前面所說的發射、反射、接收,重點就是發射超音波,接收反射的超音波,
Trig就是驅動發射超音波,Echo就是送出接收反射的超音波
依據下圖的規格,第一步就是送出10us的TTL pulse給Trig (TTL pulse是什麼),
模組就會送出8個超音波,
最後再去接收時間,(要用pulseIn這個函數,讀取收到反射超音波的時間,所以要寫HIGH)
















  digitalWrite(trigPin, LOW);
  delayMicroseconds(5);
  digitalWrite(trigPin, HIGH);     // 給 Trig 高電位,持續 10微秒
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  pinMode(echoPin, INPUT);             // 讀取 echo 的電位
  duration = pulseIn(echoPin, HIGH);   // 收到高電位時的時間


配合Otto的腳位,trig是8, echo是9,完整的測試碼如下所示

int trigPin = 8;                  //Trig Pin
int echoPin = 9;                  //Echo Pin
long duration, cm, inches;

void setup() {
  Serial.begin (9600);             // Serial Port begin
  pinMode(trigPin, OUTPUT);        //Define inputs and outputs
  pinMode(echoPin, INPUT);
}

void loop()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(5);
  digitalWrite(trigPin, HIGH);     // 給 Trig 高電位,持續 10微秒
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  pinMode(echoPin, INPUT);             // 讀取 echo 的電位
  duration = pulseIn(echoPin, HIGH);   // 收到高電位時的時間

  cm = (duration/2) / 29.1;         // 將時間換算成距離 cm 或 inch 
  inches = (duration/2) / 74;

  Serial.print("Distance : "); 
  Serial.print(inches);
  Serial.print("in,   ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();
 
  delay(250);
}