macsbug

Just another WordPress.com site

Make a voltmeter in the ADS1115 and ESP8266

with 3 comments

ESP8266 と 16bit 4ch I2C ADC ADS1115 で DC Voltmeter を作りました。             2016.03.15

基の記事はi2C 16bit ADC 4ch ADS1115 in ESP8266 を参考にしてください。
今回は 上記の内容に自動レンジ機能やキャリブレーション機能を追加し
46 x 46 x 46 mmのケースに収めました。


左:ch1=単3電池、ch2=12V Battery、ch3=ball Switch、ch4=short の電圧を測る。
右:キャリブレーション中。


準備:
ESP8266(ESP-WROOM-02)ボード:
ADS1115(紫色の基板): ebayから購入。1個331円。
SW-520D ball switch angle Tilt switch vibration switch:AliExpress1個25円。
切れる基板:3枚。[AE-D 0.3t] 秋月電子通商1枚70円。
シングルピンソケット(低メス) 1×20 :[2212S-20SG-36]秋月電商 1個60円。
ピンソケット1×8:[FH105-1x8SG/RH] 秋月電商 1個40円。
分割ロングピンソケット1×42:[FHU-1x42SG] 秋月電商 1個80円。
抵抗:100KΩ 4個 金属皮膜抵抗。秋月電子通商 1袋300円。
抵抗:10KΩ 4個 金属皮膜抵抗。秋月電子通商 1袋300円。
ピンヘッダ 2列x40P(L型,標準ピッチ) :2543-2×40 千石電商 294円。
入力コネクタ:DCジャック1.4(外形3.4)(基板用)マル信無線電機 MJ-84を使用。千石電商 74円。
入力プラグ:MJ-84に合ったDCコード 1.3(外形3.4)を使用。千石電商 263円。大きさ注意。
注:ライブラリー Adafruit_ADS1015.cpp の変更。理由:SPS速度の最適化。
_ i2C 16bit ADC 4ch ADS1115 in ESP8266 を参照願います。2カ所の修正です。
_ これを実施しない場合は読み込みデーターのチャンネルずれが起きます。
_ ADS1015_REG_CONFIG_DR_1600SPS を ADS1015_REG_CONFIG_DR_3300SPS に変更する。
_ これは 2カ所の修正です。


シールドの製作:3階立てです。
上は ch3,ch4 の入力とOLEDの端子。Ball Switch は 45度に取り付けます。
_ OLEDの配線は基板へコネクタを取り付け コネクター+ワイヤー配線を無くしました。
中は ch1,ch2 の入力端子とADS1115。
下は ESP8266。


配線:
基板は 秋月の切れる基板を使用しました。
赤い線はワイヤーで配線します。被覆カットが不要なジュンフロン線を使用。
抵抗のリード線(黒)を利用してワイヤーの配線を省略します。
OLEDとADS1115はI2C接続(SDA,SCL)ですのでGPIOの4(SDA)と5(SCL)に配線。
OLEDのSDA,SCLは内部で10KΩが内蔵されていますが念の為 2.2KΩでPull-up。
OLEDの3.3VとGND端子間に高分子コンデンサー(OS-CON)(47uf)を念の為 取り付け。
ADS1115のアドレスは ADDR=GND にし 0x48 としました。
Ball Switchは GPIO14に配線し45度で取り付け。Pull-upはスケッチにて行いました。


仕様:
_ 自動レンジ切り替え:入力の電圧に応じて自動的にゲインを変えます。
_           ゲインは 6段階あります。1 bit 0.125〜0.0078125 mV。
_ 自動有効桁数の表示:ゲインにより下3桁から下7桁までの自動表示。
_           ゲインにより有効桁数が異なる為です。
_ キャリブレーション:ch4を基準に回路及び入力ワイヤーの補正を行います。
_ 傾きセンサー   :傾きによりキャリブレーションの機能を動作させます。
_ 最大入力電圧   :36Volt 。分割抵抗(金属皮膜抵抗:100kΩ + 10kΩ を
_           使用し電源電圧 3.3Vの11倍まで可能です。
_ 電源       :USB 5Vdc。


キャリブレーション:
電源オン時にケースを水平に置くと キャリブレーションを実施します。
電源オン時にケースを傾けた場合は キャリブレーションは実施しません。
使用中にケースを傾けると キャリブレーションを実施します。
キャリブレーションは ch4 の入力を手動でショートしノイズ電圧を読み取るものです。
この値により 各chの読み取り値に補正を行います。


レンジと感度:各レンジの 1bitの電圧に 32367 を掛けると最大電圧になります。


メモ:
精度の理解がいまいちです。
_ 2/3x gain の場合は 最大+/- 6.144V の入力で、1 bit = 0.1875 mVの精度。
_ 16x gain の場合は 最大+/- 0.256V の入力で、1 bit = 0.0078125 mVの精度。

_ 電源のACノイズを測定すると3mVで抵抗分割分を加えても1bitの精度を外れる。
_ と、考えると下7桁を表示するのは意味がないかと言う事でしょうか。
_ ゲインによって精度が異なる事と、実際にどの程度が測定を実施してみると
_ 10mV位の精度でそれ以下の桁は変動しています。
_ 理解不足もありますが素人的にはこの辺にしておこうかなと思っています。

どのADCがいいか:
_ MSP3002 ADCでは mV単位が測れませんが ADS1115 はmVが測れます。
_ ADCにGAIN機能があるものは広範囲の電圧を見る事が可能という事ですね。

電源、リップル、ノイズ:
_ bit数の大きな物を使用するときは電源が気になります。
_ 1bitの電圧精度に対し如何にノイズの少ない電源を供給できるか。
_ テスターで電源のAC成分を測ると3mVのノイズがある。
_ 電源にLDO ASM1117-33を付けノイズ電圧を測ると51mVと大きく採用せず。
_ この時 前後に高分子コンデンサーを入れるが効果なく前より大きくなる。
_ OLEDの影響が僅かありOLEDの電源端子に高分子コンデンサーを取り付け。


参考:
TEXAS:ADS1115
adafuruit:ADS1115 Libraly:Adafruit_ADS1X15
adafuruit:ADS1115 Read Rate
Henry’s Beach:ARDUINO ADS1115 MODULE GETTING STARTED TUTORIAL
squix78:esp8266-oled-ssd1306


感想:
ボールスイッチは便利:角度によって接点がオンになったりオフになる物です。
_ 入力装置として通常のスイッチやタッチパネルは工作やスケッチが手間ですが
_ このスイッチは2本の線を配線するだけでスケッチも簡単です。
_ pinMode (14, INPUT_PULLUP ); GPIO端子をHIGHにする(pull-up抵抗する)
_ digitalRead(14);  この2行でできます。
_ 完成後はケースに入れますから、このスイッチを使用すると何かと便利。
_ 値段も安いのでお得ですね。尚、この手の製品で国内では1個100円します。
部品リストと価格:部品箱から使用したものが多く さほどどかからずと思いましたが
_ 完成後にリストを作ると初めての場合は結構かかるものと解る。


スケッチ:

// http://www.ti.com/lit/ds/symlink/ads1115.pdf : ADS1115:BOGI,16 bit,806 SPS
// https://github.com/adafruit/Adafruit_ADS1X15
// http://henrysbench.capnfatz.com/henrys-bench/arduino-ads1115-module-getting-started-tutorial/
// https://forums.adafruit.com/viewtopic.php?f=22&t=76452
// https://github.com/squix78/esp8266-oled-ssd1306
// Adafuruit_ADS1015.h  :ADS1115_CONVERSIONDELAY (8)   to ADS1115_CONVERSIONDELAY (9) 
// Adafuruit_ADS1015.cpp:ADS1015_REG_CONFIG_DR_1600SPS to ADS1015_REG_CONFIG_DR_3300SPS
// ADS1115 : default = GAIN_TWOTHIRDS
// ads.setGain(GAIN_TWOTHIRDS); 2/3x gain +/- 6.144V 1 bit = 0.1875    mV
// ads.setGain(GAIN_ONE);         1x gain +/- 4.096V 1 bit = 0.125     mV
// ads.setGain(GAIN_TWO);         2x gain +/- 2.048V 1 bit = 0.0625    mV
// ads.setGain(GAIN_FOUR);        4x gain +/- 1.024V 1 bit = 0.03125   mV
// ads.setGain(GAIN_EIGHT);       8x gain +/- 0.512V 1 bit = 0.015625  mV
// ads.setGain(GAIN_SIXTEEN);    16x gain +/- 0.256V 1 bit = 0.0078125 mV
// ads.begin();
// I2C ADDRESS : ADS1115 ADDR = GND : 0x48(10001000)
#include <Wire.h>                                  //
#include <Adafruit_ADS1015.h>                      // ADS1115
Adafruit_ADS1115 ads(0x48);                        // 16-bit mode
#include "SSD1306.h"                               // OLED
#include "SSD1306Ui.h"                             // OLED 
extern "C" {
  #include "user_interface.h" 
}
SSD1306 display(0x3c,4,5);                         // OLED Initialize
float g23 = 0.1875;                                // gain 2/3x
float g1  = 0.125;                                 // gain   1x
float g2  = 0.0625;                                // gain   2x
float g4  = 0.03125;                               // gain   4x
float g8  = 0.015625;                              // gain   8x
float g16 = 0.0078125;                             // gain  16x
float c   = 11;                                    // 100K + 10k ohm
float v,v1,v2,v3,v4;                               // voltage
float cal;                                         // Calibartion
int16_t r;                                         // 16 bit data
int d,d1,d2,d3,d4;                                 // digit

void setup(void) {                                 //
  display.init();                                  // OLED setup
  display.flipScreenVertically();                  // OLED setup
  display.displayOn();                             // OLED setup
  display.clear();                                 // OLED setup
  display.setFont(ArialMT_Plain_16);               // OLED setup
  display.setTextAlignment(TEXT_ALIGN_LEFT);       // OLED setup
  display.drawString(0, 0,"16bit 4ch I2C");        // title
  display.drawString(0,16,"ADS1115");              // title
  display.drawString(0,32,"Analog to Digital");    // title
  display.drawString(0,48,"Converter");            // title
  display.display();                               // title
  delay(2000);                                     // title
  display.clear();                                 // title
  display.setFont(ArialMT_Plain_24);               // title
  display.drawString(0,16,"    D.V.M");            // title
  display.display();                               // title
  pinMode (14, INPUT_PULLUP );                     // switch sens
  delay(2000);                                     //
  if ( digitalRead(14) == LOW ){ calibration();}   // calibration
}                                                  //

void loop(void) {                                  //
  if ( digitalRead(14) == HIGH){ calibration();}   // switch:calibration
  v1 = Auto_range(0); d1 = d;                      // read ch0
  v2 = Auto_range(1); d2 = d;                      // read ch1
  v3 = Auto_range(2); d3 = d;                      // read ch2
  v4 = Auto_range(3); d4 = d;                      // read ch3
  Display();                                       // OLED Display
}

float Auto_range(int ch){                                  //
  Read(23,g23,ch);                                         // +-<6.2 :2/3x gain
  if (v < 4.09 && v > -4.09){ Read( 1, g1,ch);             // +-<4.1 :  1x gain
  if (v < 2.04 && v > -2.04){ Read( 2, g2,ch);             // +-<2.1 :  2x gain
  if (v < 1.02 && v > -1.02){ Read( 4, g4,ch);             // +-<1.1 :  4x gain
  if (v < 0.52 && v > -0.52){ Read( 8, g8,ch);             // +-<0.52:  8x gain
  if (v < 0.26 && v > -0.26){ Read(16,g16,ch);             // +-<0.26: 16x gain
  }}}}}                                                    // 
  float m = 0;                                             // cal data 
  if ( v >= 0  && cal <  0 ){ m = v + cal;}                // add calibration 
  if ( v >= 0  && cal >= 0 ){ m = v - cal;}                // add calibration
  if ( v <  0  && cal <  0 ){ m = v - cal;}                // add calibration
  if ( v <  0  && cal >= 0 ){ m = v + cal;}                // add calibration
  v = m;                                                   // cal data
  return v;                                                // return voltage
}

void Read(int g,float gain,int ch){                        //
  if (g == 23){ads.setGain(GAIN_TWOTHIRDS); d=4;}          // 2/3x gain
  if (g ==  1){ads.setGain(GAIN_ONE)      ; d=3;}          //   1x gain
  if (g ==  2){ads.setGain(GAIN_TWO)      ; d=4;}          //   2x gain
  if (g ==  4){ads.setGain(GAIN_FOUR)     ; d=5;}          //   4x gain
  if (g ==  8){ads.setGain(GAIN_EIGHT)    ; d=6;}          //   8x gain
  if (g == 16){ads.setGain(GAIN_SIXTEEN)  ; d=7;}          //  16x gain
  ads.begin();                                             // gain set
  r = ads.readADC_SingleEnded(ch);                         // ch vol 16bit
  v = ( r * gain ) * c / 1000;                             // ch vol calculate
}                                                          //

void Display(){                                            // OLED Display
  display.clear();                                         //
  display.setFont(ArialMT_Plain_16);                       //
  display.setTextAlignment(TEXT_ALIGN_LEFT);               //
  display.drawString(  0, 0,"1:");                         //
  display.drawString(  0,16,"2:");                         //
  display.drawString(  0,32,"3:");                         //
  display.drawString(  0,48,"4:");                         //
  display.setTextAlignment(TEXT_ALIGN_RIGHT);              //
  display.drawString( 96, 0,String(v1,d1));                // ch 0 voltage
  display.drawString( 96,16,String(v2,d2));                // ch 1 voltage
  display.drawString( 96,32,String(v3,d3));                // ch 2 voltage
  display.drawString( 96,48,String(v4,d4));                // ch 3 voltage
  display.drawString(127, 0,"vdc");                        //
  display.drawString(127,16,"vdc");                        //
  display.drawString(127,32,"vdc");                        //
  display.drawString(127,48,"vdc");                        //
  display.display();                                       //
}                                                          //

void calibration(){                                        // ch4 cal=average
p:cal = 0;                                                 // cal start
  display.clear();                                         // 
  display.setFont(ArialMT_Plain_16);                       //
  display.setTextAlignment(TEXT_ALIGN_LEFT);               //
  display.drawString(0,00,"calibration..");                //
  display.drawString(0,32,"Short the ch4");                //
  display.display();                                       //
  delay(3000);                                             //
  for ( int i=0; i<100; i++ ){                             // 100 point 
      r = ads.readADC_SingleEnded(3);                      // ch 4 read
     v4 = ( r * g16 ) * c / 1000;                          // voltage 
    cal = cal + v4;                                        // add voltage 
    display.clear();                                       // 
    display.setTextAlignment(TEXT_ALIGN_LEFT);             // 
    display.drawString(0, 0,"zero calibration");           // 
    display.drawString(0,32,String(i));                    // 
    display.drawString(0,48,"TTL=");                       // 
    display.setTextAlignment(TEXT_ALIGN_RIGHT);            // 
    display.drawString(127,32,String(v4, 7));              // read 
    display.drawString(127,48,String(cal,7));              // total error 
    display.display();                                     //
  }                                                        // 
  cal = cal / 100;                                         // average 
  if ( cal > 0.1 || cal < -0.1 ){                          // +-0.1 limit
    display.clear();                                       // 
    display.setTextAlignment(TEXT_ALIGN_LEFT);             //
    display.drawString(0,32,"cal error");                  //
    display.display();                                     //
    delay(1000);                                           //
    goto p;                                                // re-calibration
  }                                                        //
  display.clear();                                         //
  display.setTextAlignment(TEXT_ALIGN_LEFT);               //
  display.drawString(0,48,"cal= ");                        //
  display.setTextAlignment(TEXT_ALIGN_RIGHT);              //
  display.drawString(127,48,String(cal,7));                // cal
  display.display();                                       //
  delay(2000);                                             //
}                                                          //

Written by macsbug

3月 13, 2016 @ 11:49 pm

カテゴリー: ESP8266

コメント / トラックバック3件

Subscribe to comments with RSS.

  1. こんにちは。

    ボールスイッチというものがあるのを知りませんでした。
    これだと外からアクセスできないものに対してもスイッチのON/OFFができていいですね。

    > 赤い線はワイヤーで配線します。被覆カットが不要なジュンフロン線を使用。
    僕も最近ジュンフロン線を使っているのですが、「被覆カットが不要」というのはハンダで溶かしているということでしょうか?
    ハンダ前にAWG30で被覆カットをしていました。

    7of9

    3月 19, 2016 at 12:44 am

    • こんにちわ。お元気ですか。
      はい、一番簡単で安い方法を採用しました。
      配線が2本線で済む事とebayで1個25円以下と安い。
      ケースに入れる場合、スイッチやタッチパネルを取り付けるのは手間です。
      他には、スケッチが幾つか完成すると、それらをまとめて入れたくなりますよね。
      ケースを傾けると次から次へと複数のスケッチを動かす事ができるようになります。
      ちょっと魔法的で良いかなと次の案を思案中です。

      はい、ジュンフロン線は溶かして使用しています。
      理由は、ずぼらな性格で楽をしたい為です。
      普通のワイヤーストリッパーがあるのですが切れ味が悪い性もあります。
      金属部に半田ごてをあてながら熱を伝え、溶かしながら予備半田をしてその後にハンダ付けの2ステップ。
      カッターを使用すると切る必要があり1ステップ増える訳です。

      専用ワイヤーストリッパー:線に対し真っすぐ引っ張る1万円以上するワイヤーストリッパーがあります。
      これは抜群に切れが良く切り口も傷がつかない超優れものですが、値段が高いので買うまでに至っていません。欲しい道具の1つです。

      macsbug

      3月 19, 2016 at 1:24 am

      • こんにちは。
        こちらは元気です。そちらもいかがお過ごしでしょうか。

        ボールスイッチ1個25円ですか。2本線でいいというのも簡単で良いですね。
        参考にさせていただきます。

        ジュンフロン線の使い方ですが、溶かして使うのですね。試してみたところ、確かに溶かしてはんだ付けできました。検索してみた所、他の方も同じように溶かしている例がありました。
        ワイヤストリッパーではきちんと切れないという例もあり、実際僕が使っているAWG30対応のワイヤストリッパー(Hozan P-967)でも切れ味は良くありません。また、うまく切らないと芯線が痛むという意見もありました。
        今後は溶かして実装する方法を真似させていただきます。

        OLEDの記事も興味深いですね。自分でライブラリを改良されているというのもすごいです。
        また参考にさせていただきます。

        7of9

        3月 21, 2016 at 4:38 am


コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。