ESP32 (4) mini-Oscilloscope

Arduino UNOと同じmini-Oscilloscope を作って見た。

表示の正確性はまた検証が必要だけど

ソースコードはほぼ一緒

/*
This is set up to use a 128x64 I2C screen, as available
here: http://www.banggood.com/buy/0-96-oled.html
For wiring details see http://youtu.be/XHDNXXhg3Hg
*/

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#if (SSD1306_LCDHEIGHT != 64)
//  error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

/********************************************/

#define CHARWIDTH           5
#define CHARHEIGHT          8
#define AXISWIDTH           (2 + 1)                   // axis will show two-pixel wide graph ticks, then an empty column
#define VISIBLEVALUEPIXELS  (128 - AXISWIDTH)         // the number of samples visible on screen
#define NUMVALUES           (2 * VISIBLEVALUEPIXELS)  // the total number of samples (take twice as many as visible, to help find trigger point

#define TRIGGER_ENABLE_PIN       2  // set this pin high to enable trigger
#define SCREEN_UPDATE_ENABLE_PIN 3  // set this pin high to freeze screen

byte values[NUMVALUES];           // stores read analog values mapped to 0-63
int pos = 0;                      // the next position in the value array to read
int count = 0;                    // the total number of times through the loop
unsigned long readStartTime = 0;  // time when the current sampling started
int sampleRate = 1;              // A value of 1 will sample every time through the loop, 5 will sample every fifth time etc.

const int voutPin = 2;
const int VOLT = 3.3; // 3.3Vを電源とした場合
const int ANALOG_MAX = 4096; // ESP32の場合


/********************************************/

// Draws a printf style string at the current cursor position
void displayln(const char* format, ...)
{
  char buffer[32];
  
  va_list args;
  va_start(args, format);
  vsprintf(buffer, format, args);
  va_end(args);
  
  int len = strlen(buffer);
  for (uint8_t i = 0; i < len; i++) {
    display.write(buffer[i]);
  }
}

// Draws the graph ticks for the vertical axis
void drawAxis()
{  
  // graph ticks
  for (int x = 0; x < 2; x++) {
    display.drawPixel(x,  0, WHITE);
    display.drawPixel(x, 13, WHITE);
    display.drawPixel(x, 26, WHITE);
    display.drawPixel(x, 38, WHITE);
    display.drawPixel(x, 50, WHITE);
    display.drawPixel(x, 63, WHITE);  
  }
}

// Draws the sampled values
void drawValues()
{
  int start = 0;
  
  if ( digitalRead(TRIGGER_ENABLE_PIN) ) {
    // Find the first occurence of zero
    for (int i = 0; i < NUMVALUES; i++) {
      if ( values[i] == 0 ) {
        // Now find the next value that is not zero
        for (; i < NUMVALUES; i++) {
          if ( values[i] != 0 ) {
            start = i;
            break;
          }
        }
        break;
      }
    }    
    // If the trigger point is not within half of our values, we will 
    // not have enough sample points to show the wave correctly
    if ( start >= VISIBLEVALUEPIXELS )
      return;
  }
  
  for (int i = 0; i < VISIBLEVALUEPIXELS; i++) {
    display.drawPixel(i + AXISWIDTH, 63 - (values[i + start]), WHITE);
  }
}

// Shows the time taken to sample the values shown on screen
void drawFrameTime(unsigned long us)
{
  display.setCursor(9 * CHARWIDTH, 7 * CHARHEIGHT - 2); // almost at bottom, approximately centered
  displayln("%ld us", us);
}

/********************************************/

void setup() {

  // Set up the display
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Initialize with the I2C addr 0x3D (for the 128x64)
  // Clear the buffer.
  display.clearDisplay();
  display.setTextColor(WHITE);

  pinMode(TRIGGER_ENABLE_PIN, INPUT);
  pinMode(SCREEN_UPDATE_ENABLE_PIN, INPUT);
}

/********************************************/

void loop() {
  
  // If a sampling run is about to start, record the start time
  if ( pos == 0 )
    readStartTime = micros();
  
  // If this iteration is one we want a sample for, take the sample
  if ( (++count) % sampleRate == 0 )
    values[pos++] = analogRead(voutPin) >> 6; // shifting right by 4 efficiently maps 0-1023 range to 0-63

  // If we have filled the sample buffer, display the results on screen
  if ( pos >= NUMVALUES ) {
    // Measure how long the run took
    unsigned long totalSampleTime = (micros() - readStartTime) / 2;     // Divide by 2 because we are taking twice as many samples as are shown on the screen
 
//    if ( !digitalRead(SCREEN_UPDATE_ENABLE_PIN) ) {
      // Display the data on screen   
      display.clearDisplay();
      drawAxis();
      drawValues();
      drawFrameTime(totalSampleTime);
      display.display();
//    }
       
    // Reset values for the next sampling run
    pos = 0;
    count = 0;
  }
}

 

 

Digispark (3) Timer

Timerの仕組み

TM1637で残り時間の表示。

結線

  • GND—GND
  • VCC— VCC
  • CLK — D2
  • DIO — D1

コード

コードは参考1から利用

#define CLK 2
#define DIO 1

参考

  1. https://www.electroschematics.com/13138/matchbox-sized-digital-timer/

 

Digispark (2) NeoPixel

NeoPixelとは

WS2812 LED stripだ

結線

電源の他に、DataはP0につなぐ。

コード

コードは参考1から利用

https://github.com/smartynov/iotfun/tree/master/arduino/deco_lights

WS2812は8個内蔵だから、下記のように変更する

#define PIN 0
#define NUMPIXELS 8

また64個内蔵の場合、BRIGHTNESS を弱めて、下記のように変更する。

#include <Adafruit_NeoPixel.h>

// set to pin connected to data input of WS8212 (NeoPixel) strip
#define PINGND      0
#define PIN         1
#define PINVCC      2

// any pin with analog input (used to initialize random number generator)
#define RNDPIN      2

// number of LEDs (NeoPixels) in your strip
// (please note that you need 3 bytes of RAM available for each pixel)
#define NUMPIXELS   64

// max LED brightness (1 to 255) – start with low values!
// (please note that high brightness requires a LOT of power)
#define BRIGHTNESS  32

// increase to get narrow spots, decrease to get wider spots
#define FOCUS       65

// decrease to speed up, increase to slow down (it's not a delay actually)
#define DELAY       4000

// set to 1 to display FPS rate
#define DEBUG       0


Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// we have 3 color spots (reg, green, blue) oscillating along the strip with different speeds
float spdr, spdg, spdb;
float offset;

#if DEBUG
// track fps rate
long nextms = 0;
int pfps = 0, fps = 0;
#endif

// the real exponent function is too slow, so I created an approximation (only for x < 0)
float myexp(float x) {
  return (1.0/(1.0-(0.634-1.344*x)*x));
}


void setup() {
  pinMode(PINVCC, OUTPUT);
  digitalWrite(PINVCC, HIGH);
  pinMode(PINGND, OUTPUT);
  digitalWrite(PINGND, LOW);
  // initialize pseudo-random number generator with some random value
  randomSeed(analogRead(RNDPIN));

  // assign random speed to each spot
  spdr = 1.0 + random(200) / 100.0;
  spdg = 1.0 + random(200) / 100.0;
  spdb = 1.0 + random(200) / 100.0;

  // set random offset so spots start in random locations
  offset = random(10000) / 100.0;

  // initialize LED strip
  strip.begin();
  strip.show();
}

void loop() {
  // use real time to recalculate position of each color spot
  long ms = millis();
  // scale time to float value
  float m = offset + (float)ms/DELAY;
  // add some non-linearity
  m = m - 42.5*cos(m/552.0) - 6.5*cos(m/142.0);

  // recalculate position of each spot (measured on a scale of 0 to 1)
  float posr = 0.5 + 0.55*sin(m*spdr);
  float posg = 0.5 + 0.55*sin(m*spdg);
  float posb = 0.5 + 0.55*sin(m*spdb);

  // now iterate over each pixel and calculate it's color
  for (int i=0; i<NUMPIXELS; i++) {
    // pixel position on a scale from 0.0 to 1.0
    float ppos = (float)i / NUMPIXELS;
 
    // distance from this pixel to the center of each color spot
    float dr = ppos-posr;
    float dg = ppos-posg;
    float db = ppos-posb;
 
    // set each color component from 0 to max BRIGHTNESS, according to Gaussian distribution
    strip.setPixelColor(i,
      constrain(BRIGHTNESS*myexp(-FOCUS*dr*dr),0,BRIGHTNESS),
      constrain(BRIGHTNESS*myexp(-FOCUS*dg*dg),0,BRIGHTNESS),
      constrain(BRIGHTNESS*myexp(-FOCUS*db*db),0,BRIGHTNESS)
      );
  }

#if DEBUG
  // keep track of FPS rate
  fps++;
  if (ms>nextms) {
    // 1 second passed – reset counter
    nextms = ms + 1000;
    pfps = fps;
    fps = 0;
  }
  // show FPS rate by setting one pixel to white
  strip.setPixelColor(pfps,BRIGHTNESS,BRIGHTNESS,BRIGHTNESS);
#endif

  // send data to LED strip
  strip.show();
}

 

参考

  1. https://www.instructables.com/id/USB-NeoPixel-Deco-Lights-via-Digispark-ATtiny85/

Digispark (1) Blink

Digisparkとは


アメリカ Digistump LCCの製品。
AVRマイクロコントローラーAtmel Attiny85を搭載しており、小さいながらもArduino IDE(開発環境)を使用し、プログラミングを行うことができる。

 

 

開発環境

Arduinoソフトウェアをインストール

Arduinoのソフトウェアをダウンロード(Ver.1.6.6)して、インストールする。

ボードパッケージをインストール

Digisparkのボードパッケージ(Digistump AVR Board)をインストール。

http://digistump.com/package_digistump_index.json

Digistump AVR Boards by Digistumpというのがあるのでそこをクリックしてインストール。

Window10

Windows10の場合、ボードパッケージをインストールと次のように警告メッセージが出る。

警告:信頼されていないコントリビューションです。スクリプトの実行をスキップしています(C:\Users\%USERNAME%\Documents\ArduinoData\packages\digistump\tools\micronucleus\2.0a4\post_install.bat)

このパスをコピーし、手動でインストールした。

Windows7

Digisparkのドライバー(micronucleus-2.0a4-win.zip)をダウンロードして、インストールする必要。

Lチカ コード

Blink点滅

// the setup routine runs once when you press reset:
bool led_stat = false;

void setup(){
  pinMode(0, true);
  pinMode(1, true);
}

void loop(){
  digitalWrite(0, led_stat);
  digitalWrite(1, !led_stat);
  led_stat = !led_stat;
  delay(500);
}

Fade効果

void setup(){
}

void loop(){
  for(char i = 0; i < 26; i++){
    analogWrite(0, i*10);
    analogWrite(1, i*10);
    delay(20);
  }
  for(char i = 25; i > 0; i--){
    analogWrite(0, i*10);
    analogWrite(1, i*10);
    delay(20);
  }
}

 

マイコンボード:から、Digispark (Default – 16.5mhz) を選択。

書き込み手順:
– 検証でcode自体が通るものであることを確認しておく
– hardwareを抜いた状態で待機
– マイコンボードに書き込むボタンを押す
– 最大60秒のhardware認識待ち状態になる
– hardwareを差す
– 書き込みができる
– 完了!!

 

参考

  1. https://www.elefine.jp/digispark/index.html

NodeMcu (4) ADS1115 & OLED

経緯

アナログ信号を取り込み、TinyWebDBへ送るプログラムを作る予定。サンプリングレートは秒に100回。

WeMos miniは使いやすいから愛用しているが、2月から、WeMos miniの開発中も頻繁にリブートしています。デバイストライバのバージョンを変えたり、OSを変えたりしても改善しない。結局原因がわからず、仕方なく開発中書き込み直前にUSBケーブル接続、書き込み終わったらすぐUSBケーブ抜く方法で運がよければリブート回避する状態が続て、悩んでいます。

DS1115プログラムを作るため、安定したNodeMcuできることならこちらで凌ぎ。

DS1115とは

DS1115 は、16-Bit ADC – 4 Channel with Programmable Gain Amplifier
特徴:広い電源電圧範囲:2.0V ~ 5.5V。低消費電流: 連続モード:150μA。
_ プログラマブルゲインアンプ内蔵。プログラミング可能なコンパレータ。
_ 4本のシングルエンド入力。2本の差動入力。I2Cインターフェイス。
_ 最小±256mVから電源電圧までの入力範囲に対応。

ライブラリーでは コマンド送信し 8msec 後に データーの読み込みを行う、サンプリングレートは秒に100回するため、Adafruit_ADS1X15/Adafruit_ADS1015.hのADS1115_CONVERSIONDELAYを8から9に変更する。

結線

普通のI2Cでつなぐだけ。

スケッチ

最初のプログラムは、アナログ読む度に表示してるが、表示できるのは秒に十回未満。そしてOLEDには、1秒間読み取れたデータの個数と、最新のアナログ値を表示すると変更して110回程度読み込みできるようになり。

ADS1115_CONVERSIONDELAYを8から9に変更して、表示の通り、秒に101回って、概ね要求に満たした。

#include <Wire.h>
#include <Adafruit_ADS1015.h>               // ADS1015,1115
Adafruit_ADS1115 ads(0x48);                 // 16-bit version
#include <Adafruit_GFX.h>                   // OLED
#include <Adafruit_SSD1306.h>           // OLED 
Adafruit_SSD1306 display(0);                // OLED Reset
 
void setup(void) {
  //Serial.begin(115200);
//  Wire.begin(4,5);                          // OLED:SDA,SCL
  display.begin(SSD1306_SWITCHCAPVCC,0x3c); // I2C ADDRESS=3c
  display.clearDisplay();                   // Clear the buffer
  display.setTextSize(2);                   // font size 4
  display.setTextColor(WHITE);              //
  display.setCursor(0,0);                   //
  ads.setGain(GAIN_TWOTHIRDS);              // 2/3x gain +/-6.144V
  ads.begin();                              // 1 bit=0.1875mV
}
 
void loop(void) {
  int i=0;
  float v0;
  // // Count and Store data during 1,000 msec
  for(int time = millis(); millis()-time < 1000; i++) {
    v0 = (ads.readADC_SingleEnded(0) * 0.1875/1000); // A0 Read
  }
  display.clearDisplay(); 
  display.setCursor(0,0);
  display.print(i);
  display.setCursor(0,16);
  display.println(v0,3);
  display.display(); 
}

 

参考

  1. https://macsbug.wordpress.com/2016/02/04/i2c-adc-ads1115-in-esp8266/

ESP32 (3) analogRead

目的

12bit A/Dは複数内蔵するので、活用したい。

スケッチはArduino Unoと同じような記述でできる。ただし読み取り値は0〜1023ではなく0〜4096で、入力電圧の範囲は0〜3.3Vのようだ。

結線

 

スケッチ

const int voutPin = 2;
const int VOLT = 3.3; // 3.3Vを電源とした場合
const int ANALOG_MAX = 4096; // ESP32の場合

void setup() {
  Serial.begin(115200);
}

void loop() {
  // R1の電圧を取得
  int reading = analogRead(voutPin);

  // AD値をmVに変換
  float voltage = ((long)reading * VOLT * 1000) / ANALOG_MAX;

  Serial.print(voltage);
  Serial.println(" mV, ");
  
  delay(1000);
}

シリアルモニタを立ち上げると1秒ごとに読み取った値が表示。

結果

電池の電圧を測ると、概ねに正常範囲の値出るが、値は常に変化するので、どうして?

AnaRead.png

参考

  1. http://rikoubou.hatenablog.com/entry/2017/06/29/135819 — 【ESP32】analogReadする方法