macsbug

Make a case of M5STACK ESP32 Oscilloscope

with 4 comments

M5STACK ESP32 Oscilloscope のケースを作りました。       2018.04.03
rev 1. 2018.08.01
rev 2. 2019.12.01 Prevent reboot every 5 seconds.

botofan calin氏 作成の「M5Stack-ESP32-Oscilloscope」が
2018年3月31日に公開されました。待ち望んだアプリの公開です。
botofan calin氏 に 感謝致します。

費用:1196円。大きな費用は BNC コネクター 4個で400円です。

Input は 2CH ( GPIO 35, 36) で Output は 2CH ( GPIO 2, 5 )です。

仕様:メニューは 以下の項目があり SELECT で 詳細な設定ができます。

  1. Run/Stop
  2. Channel 1 range : 1, 0.5, 0.2, 0.1V, 50mV
  3. Channel 2 range : 1, 0.5, 0.2, 0.1V, 50mV
  4. Channel 1 and 2 rate : 5, 10, 20ms/DIV
  5. Channel 1 mode (Normal, Inverted, Off)
  6. Channel 2 mode (Normal, Inverted, Off)
  7. Channel 1 Offset
  8. Channel 2 Offset
  9. Trigger Channel : 1 or 2
  10. Trigger mode Auto, Normal, Scan
  11. Trigger Level
  12. Trigger Edge: Rising (UP) or Falling (DN)

追記:2018.04.06 「M5Stack-ESP32-Oscilloscope」のアップデートがありました。
_   MENU が追加され 操作しやすくなりました。
_  

使用例:左は INPUT。右下は OUTPUT です。


準備:

No NOMEN 販売店 価格 Note
1  OscilloScope PROT Frame (高さ 15mm)  自作(59分)    19円 OBJET
1  OscilloScope PROT Frame (高さ 15mm)  自作(73分)    11円 uPrint
1  OscilloScope PROT Frame (高さ 15mm)  自作(48分)      0円 Replicator
2  Battery PROT Frame          (高さ 6.5mm)  自作(31分)    11円 OBJET
3  Rear Cover PROT Frame   (高さ 4mm)  自作(42分)    55円 OBJET
4   0.3mm厚基板 C Type  72x47mm  秋月電子通商  120円 0.3mm厚
5   1.0mm厚基板 C Type  72x47mm  秋月電子通商  120円 1.0mm厚
6  ピンヘッダ (オス) 2×30 (60P)  秋月電子通商    25円
7  ロープロファイルピンソケット 2×15, 30P  秋月電子通商    40円
8  ピンソケット 2×20 40P  秋月電子通商   150円
9  BNC コネクタ ( 1個 100円) 4 個  秋月電子通商  400円
10  M2 Hex Head Tapping Screw (8 ea )  ebay    56円
11  BATTERY NP-40  3.7V 1300mA  杉元ガレージ  200円  秋葉原
12  RF ケーブル, ワイヤー等  在庫品       0円
 —————————————  —————-  ——-  ———–
TOTAL  1196円 OBJET

他の部品:

No NOMEN 販売店 価格 Note
1  オシロスコープ ケーブル  CompuAce 秋葉原
1  BNC コネクター  ラジオストアー2F 山本電機  150円 秋葉原
1  BNC SMA 変換コネクター  ラジオストアー2F 山本電機  250円 秋葉原
1  BNC SMA 変換コネクター  CompuAce  124円 秋葉原
2  RFコネクターケーブル  CompuAce 秋葉原

 

部品の入手方法:
コネクター:
_ 一番安い店は 地下鉄「末広町駅」近く、秋葉原の「CompuAce」です。
バッテリー:
_ カメラ用の四角いものが最適です。ただし 通常は高いです。
_ 秋葉原の杉元ビル杉元ガレージ (中古品)で 200〜300円で入手できます。
_ このバッテリーは M5STACK の PROT Module の大きさに入ります。
_ 容量のあるバッテリーを使用すると便利になります。バッテリーは 価格が高い為
_  事前に品質良く、安い製品を探しておくと良いです。新品で購入しやすい製品は
_  ロワ・ジャパン ROWA @ JAPAN の NP-70 1250mAh Li-ion です。


組み立て:
_ INPUT は GPIO 35, 36 へ配線します。OUTPUT は GPIO 2, 5 へ配線します。

   
   

 

メモ:
コネクターの高さ:
_ スタック構造ですので コネクターの高さが重要です。
_ 接続コネクターによって 高さが異なることや インターフェースの高さも影響します。
_ これらを考慮して 幾つかの FRAME や コネクターを用意して 高さを調整します。
_ 基板の厚さも重要です。 使用しているのは 1mm厚と 0.3mm厚の基板です。

_ SG = 1KHz の例:twitter: 周波数範囲を理解して使用します。
_ 


再設計:2018.08.01
M5STACK OSCILLOSCOPE PROTO :
部品は全て国内部品を使用し 再設計しました。
ケース高 15mm + ピンソケット2×15 5.7mm x 2個 でスタック可能です。


感想:
botofan calin氏 作成の「M5Stack-ESP32-Oscilloscope」は 多数の項目があり
_ それぞれの項目で電圧やレンジを変更できます。操作して その出来具合に
_ 感心しています。
_ M5SATCK の目的の1つにオシロスコープの製作がありますが
_ botofan calin氏により 作成されました。 私の自作案より 遥かに完成度が高く
_ 感激しています。botofan calin氏に感謝致します。
_ 公開当初、スピーカーから異音がでる不具合がありましたが その後 他の件
_ も含め Revision が実施されています。commit には 多数の Revision があり
_ 敬服致します。
_ M5STACK は ケースに入っているので使用しています。それに合わせて
_  綺麗なケースを作りたくなります。


スケッチ:
// rev 2. 2019.12.01
// https://macsbug.wordpress.com/2018/04/03/make-a-case-of-m5stack-oscilloscope/

// 2019.12.01 : Prevent reboot every 5 seconds : macsbug
// 2018.04.03 : macsbug :
//   https://macsbug.wordpress.com/2018/04/03/make-a-case-of-m5stack-oscilloscope/
// M5Stack Community : 
//   http://forum.m5stack.com/topic/147/m5stack-2-channel-oscilloscope/20
// 2 May 2018 : botofancalin
//   https://github.com/botofancalin/M5Stack-ESP32-Oscilloscope

#include <M5Stack.h>
#include "M5StackUpdater.h"
#include <esp32/ulp.h>     // ULP : Prevent reboot every 5 seconds. rev 2019.11.30
#include <driver/rtc_io.h> // ULP : 
#include <driver/dac.h>    // ULP : 
#include <soc/rtc.h>       // ULP : 
#include <math.h>          // ULP : 
const int LCD_WIDTH  = 320;
const int LCD_HEIGHT = 240;
const int SAMPLES    = 320;
const int DOTS_DIV   =  30;

const int ad_ch0 = 35; // Analog 35 pin for channel 0
const int ad_ch1 = 36; // Analog 36 pin for channel 1
const long VREF[] = { 250, 500, 1250, 2500, 5000 };
const int MILLIVOL_per_dot[] = { 33, 17, 6, 3, 2 };
const int MODE_ON   = 0;
const int MODE_INV  = 1;
const int MODE_OFF  = 2;
const char *Modes[] = {"NORM","INV","OFF"};
const int TRIG_AUTO = 0;
const int TRIG_NORM = 1;
const int TRIG_SCAN = 2;
const char *TRIG_Modes[] = {"Auto","Norm","Scan"};
const int TRIG_E_UP = 0;
const int TRIG_E_DN = 1;
#define RATE_MIN 0
#define RATE_MAX 13
const char *Rates[] = {"F1-1","F1-2","  F2"," 5ms","10ms","20ms","50ms",
                       "0.1s","0.2s","0.5s","1s","2s","5s","10s"};
#define RANGE_MIN 0
#define RANGE_MAX 4
const char *Ranges[] = {" 1V","0.5V","0.2V","0.1V","50mV" };
int   range0 = RANGE_MIN;
short range1 = RANGE_MIN;
short ch0_mode = MODE_ON;
short ch0_off = 0;
short ch1_mode = MODE_ON;
short ch1_off = 0;
short rate = 3;
short trig_mode = TRIG_AUTO;
short trig_lv = 40;
short trig_edge = TRIG_E_UP;
short trig_ch = 0;
short Start = 1;
short menu = 19;
short data[4][SAMPLES]; // keep twice of the number of channels to make it a double buffer
short sample = 0;       // index for double buffer
int   amplitude = 0;
int   amplitudeStep = 5;

TaskHandle_t LedC_Gen;
TaskHandle_t SigmaDeltaGen;

///////////////////////////////////////////////////////////////////////////////////////////////
#define CH1COLOR YELLOW
#define CH2COLOR CYAN
#define GREY 0x7BEF
//---------------------------------------------------------------------------------------------
void DrawText(){
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setTextSize(1);
  M5.Lcd.fillRect(270, 19, 70, 121, BLACK);
  M5.Lcd.fillRect(270, menu, 70, 10, BLUE);
  M5.Lcd.setCursor(270, 20);
  M5.Lcd.println(Start == 0 ? "Stop" : "Run");
  M5.Lcd.setCursor(270, 30);
  M5.Lcd.println(String(String(Ranges[range0]) + "/DIV"));
  M5.Lcd.setCursor(270, 40);
  M5.Lcd.println(String(String(Ranges[range1]) + "/DIV"));
  M5.Lcd.setCursor(270, 50);
  M5.Lcd.println(String(String(Rates[rate]) + "/DIV"));
  M5.Lcd.setCursor(270, 60);
  M5.Lcd.println(Modes[ch0_mode]);
  M5.Lcd.setCursor(270, 70);
  M5.Lcd.println(Modes[ch1_mode]);
  M5.Lcd.setCursor(270, 80);
  M5.Lcd.println("OFS1:" + String(ch0_off));
  M5.Lcd.setCursor(270, 90);
  M5.Lcd.println("OFS2:" + String(ch1_off));
  M5.Lcd.setCursor(270, 100);
  M5.Lcd.println(trig_ch == 0 ? "T:1" : "T:2");
  M5.Lcd.setCursor(270, 110);
  M5.Lcd.println(TRIG_Modes[trig_mode]);
  M5.Lcd.setCursor(270, 120);
  M5.Lcd.println("Tlv:" + String(trig_lv));
  M5.Lcd.setCursor(270, 130);
  M5.Lcd.println((trig_edge == TRIG_E_UP) ? "T:UP" : "T:DN");
}
//---------------------------------------------------------------------------------------------
void CheckSW(){
  M5.update();
  if (M5.BtnB.wasPressed()){(menu < 129) ? (menu += 10) : (menu = 19);return;
  }else if (M5.BtnA.wasPressed()){
    switch (menu){
    case  19:if (Start == 0){Start = 1;}else{Start = 0;}break;
    case  29:if (range0 > 0){range0--;}break;
    case  39:if (range1 > 0){range1--;}break;
    case  49:if (rate > 0){rate--;}break;
    case  59:if (ch0_mode > 0){ch0_mode--;}break;
    case  69:if (ch1_mode > 0){ch1_mode--;}break;
    case  79:if (ch0_off > -1023){ch0_off -= 1024 / VREF[range0];}break;
    case  89:if (ch1_off > -1023){ch1_off -= 1024 / VREF[range1];}break;
    case  99:if (trig_ch == 0){trig_ch = 1;}else{trig_ch = 0;}break;
    case 109:if (trig_mode > 0){trig_mode--;}else{trig_mode = TRIG_SCAN;}break;
    case 119:if (trig_lv > 0){trig_lv--;}break;
    case 129:if (trig_edge == TRIG_E_UP){trig_edge = TRIG_E_DN;}else{trig_edge = TRIG_E_UP;}break;
    }
    return;
  }
  if (M5.BtnC.wasPressed()){
    switch (menu){
    case  19:if (Start == 0){Start = 1;}else{Start = 0;}break;
    case  29:if (range0 < RANGE_MAX){range0++;}break;
    case  39:if (range1 < RANGE_MAX){range1++;}break;
    case  49:if (rate < RATE_MAX){rate++;}break;
    case  59:if (ch0_mode < 2){ch0_mode++;}break;
    case  69:if (ch1_mode < 2){ch1_mode++;}break;
    case  79:if (ch0_off < 1023){ch0_off += 1024 / VREF[range0];}break;
    case  89:if (ch1_off < 1023){ch1_off += 1024 / VREF[range1];}break;
    case  99:if (trig_ch == 0){trig_ch = 1;}else{trig_ch = 0; }break;
    case 109:if (trig_mode < TRIG_SCAN){trig_mode++;}else{trig_mode = 0;}break;
    case 119:if (trig_lv < 60){trig_lv++;}break;
    case 129:if (trig_edge == TRIG_E_UP){trig_edge = TRIG_E_DN;}else{trig_edge = TRIG_E_UP;}break;
    }
    return;
  }else{return;}
  DrawText();
}
//---------------------------------------------------------------------------------------------
void DrawGrid(){
  for (int x = 0; x <= SAMPLES; x += 2){ // Horizontal Line{
    for (int y = 0; y <= LCD_HEIGHT; y += DOTS_DIV){M5.Lcd.drawPixel(x, y, GREY);CheckSW();}
    if (LCD_HEIGHT == 240){M5.Lcd.drawPixel(x,LCD_HEIGHT - 1,GREY);}
  }
  for (int x = 0; x <= SAMPLES; x += DOTS_DIV){ // Vertical Line{
    for (int y = 0; y <= LCD_HEIGHT; y += 2){M5.Lcd.drawPixel(x, y, GREY);CheckSW();}
  }
}
//---------------------------------------------------------------------------------------------
void DrawGrid(int x){
  if ((x % 2) == 0){
   for (int y = 0; y <= LCD_HEIGHT;y += DOTS_DIV){M5.Lcd.drawPixel(x,y,GREY);}
  }
  if ((x % DOTS_DIV) == 0){
    for (int y = 0; y <= LCD_HEIGHT; y += 2){M5.Lcd.drawPixel(x, y, GREY);}
  }
}
//---------------------------------------------------------------------------------------------
void ClearAndDrawGraph(){
  int clear = 0;
  if (sample == 0){clear = 2;}
  for (int x = 0; x < (SAMPLES - 1); x++){
    M5.Lcd.drawLine(x, LCD_HEIGHT - data[clear + 0][x], x + 1, LCD_HEIGHT - data[clear + 0][x + 1], BLACK);
    M5.Lcd.drawLine(x, LCD_HEIGHT - data[clear + 1][x], x + 1, LCD_HEIGHT - data[clear + 1][x + 1], BLACK);
    if (ch0_mode != MODE_OFF){
      M5.Lcd.drawLine(x, LCD_HEIGHT - data[sample + 0][x], x + 1, LCD_HEIGHT - data[sample + 0][x + 1], CH1COLOR);
    }
    if (ch1_mode != MODE_OFF){
      M5.Lcd.drawLine(x, LCD_HEIGHT - data[sample + 1][x], x + 1, LCD_HEIGHT - data[sample + 1][x + 1], CH2COLOR);
    }
    CheckSW();
  }
}
//---------------------------------------------------------------------------------------------
void ClearAndDrawDot(int i){
  int clear = 0;
  if (i <= 1){return;}
  if (sample == 0){clear = 2;}
  M5.Lcd.drawLine(i - 1, LCD_HEIGHT - data[clear + 0][i - 1], i, LCD_HEIGHT - data[clear + 0][i], BLACK);
  M5.Lcd.drawLine(i - 1, LCD_HEIGHT - data[clear + 1][i - 1], i, LCD_HEIGHT - data[clear + 1][i], BLACK);
  if (ch0_mode != MODE_OFF){
    M5.Lcd.drawLine(i - 1, LCD_HEIGHT - data[sample + 0][i - 1], i, LCD_HEIGHT - data[sample + 0][i], CH1COLOR);
  }
  if (ch1_mode != MODE_OFF){
    M5.Lcd.drawLine(i - 1, LCD_HEIGHT - data[sample + 1][i - 1], i, LCD_HEIGHT - data[sample + 1][i], CH2COLOR);
  }
  DrawGrid(i);
}
//---------------------------------------------------------------------------------------------
void DrawGraph(){
  for (int x = 0; x < SAMPLES; x++){
    M5.Lcd.drawPixel(x, LCD_HEIGHT - data[sample + 0][x], CH1COLOR);
    M5.Lcd.drawPixel(x, LCD_HEIGHT - data[sample + 1][x], CH2COLOR);
  }
}
//---------------------------------------------------------------------------------------------
void ClearGraph(){
  int clear = 0;
  if (sample == 0){clear = 2;}
  for (int x = 0; x < SAMPLES; x++){
    M5.Lcd.drawPixel(x, LCD_HEIGHT - data[clear + 0][x], BLACK);
    M5.Lcd.drawPixel(x, LCD_HEIGHT - data[clear + 1][x], BLACK);
  }
}
//---------------------------------------------------------------------------------------------
inline long adRead(short ch, short mode, int off){
  long a = analogRead(ch);
  a = (((a + off) * VREF[(ch == ad_ch0) ? range0 : range1]) / 10000UL) + 30;
  a = ((a >= LCD_HEIGHT) ? LCD_HEIGHT : a);
  if (mode == MODE_INV){return LCD_HEIGHT - a;}
  return a;
}
//---------------------------------------------------------------------------------------------
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255){
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);
  ledcWrite(channel, duty);
}
//---------------------------------------------------------------------------------------------
// Make a PWM generator task on core 0
// Signal generator pin 2
void LedC_Task(void *parameter){
  ledcSetup(0, 50, 13);
  ledcAttachPin(2, 0);
  for (;;){
    ledcAnalogWrite(0, amplitude);
    amplitude = amplitude + amplitudeStep;
    if (amplitude <= 0 || amplitude >= 255){amplitudeStep = -amplitudeStep;}
    delay(30);
  }
  vTaskDelete(NULL);
}
//---------------------------------------------------------------------------------------------
void SigmaDelta_Task(void *parameter){
  sigmaDeltaSetup(0, 312500);
  sigmaDeltaAttachPin(5, 0);
  sigmaDeltaWrite(0, 0);
  for (;;){
    static uint8_t i = 0;
    sigmaDeltaWrite(0, i++);
    delayMicroseconds(50);
  }
}
//---------------------------------------------------------------------------------------------
void setup(){
  M5.begin();
  dacWrite(25, 0); // Speaker OFF
  Wire.begin(); 
  if(digitalRead(BUTTON_A_PIN)==0){updateFromFS(SD);ESP.restart();}
  M5.Lcd.fillScreen(BLACK);
  disableCore0WDT(); // Prevent reboot every 5 seconds. rev 2019.11.30
  disableCore1WDT(); // Prevent reboot every 5 seconds. rev 2019.11.30
  DrawGrid();
  DrawText();
  M5.Lcd.setBrightness(100);

  xTaskCreatePinnedToCore(
    LedC_Task,         /* Task function. */
    "LedC_Task",       /* name of the task, a name just for humans */
    8192,                    /* Stack size of task */
    NULL,                    /* parameter of the task */
    2,                       /* priority of the task */
    &LedC_Gen,               /* Task handle to keep track of the created task */
    0);                      /*cpu core number where the task is assigned*/

  xTaskCreatePinnedToCore(
    SigmaDelta_Task,     /* Task function. */
    "SigmaDelta_Task",     /* name of task, a name just for humans */
    8192,                    /* Stack size of task */
    NULL,                    /* parameter of the task */
    2,                       /* priority of the task */
    &SigmaDeltaGen,          /* Task handle to keep track of the created task */
    0);                      /*cpu core number where the task is assigned*/
}
//---------------------------------------------------------------------------------------------
void loop(){
  if (trig_mode != TRIG_SCAN){
    unsigned long st = millis();
    short oad;
    if (trig_ch == 0){oad = adRead(ad_ch0, ch0_mode, ch0_off);
    }else{oad = adRead(ad_ch1, ch1_mode, ch1_off);}
    for (;;){
      short ad;
      if (trig_ch == 0){ad = adRead(ad_ch0, ch0_mode, ch0_off);
      }else{ad = adRead(ad_ch1, ch1_mode, ch1_off);}

      if (trig_edge == TRIG_E_UP){
        if (ad >= trig_lv && ad > oad){break;}
      }else{if (ad <= trig_lv && ad < oad){break;}}
      oad = ad;
      CheckSW();
      if (trig_mode == TRIG_SCAN){break;}
      if (trig_mode == TRIG_AUTO && (millis() - st) > 100){break;}
    }
  }

  // sample and draw depending on the sampling rate
  if (rate <= 5 && Start){
    // change the index for the double buffer
    if (sample == 0){sample = 2;}else{sample = 0;}
    if (rate == 0){ // full speed, channel 0 only
      unsigned long st = millis();
      for (int i = 0; i < SAMPLES; i++){
        data[sample + 0][i] = adRead(ad_ch0, ch0_mode, ch0_off);
      }
      for (int i = 0; i < SAMPLES; i++){data[sample + 1][i] = 0;}
     // full speed, channel 1 only
    }else if (rate == 1){
      unsigned long st = millis();
      for (int i = 0; i < SAMPLES; i++){
        data[sample + 1][i] = adRead(ad_ch1, ch1_mode, ch1_off);
      }
      for (int i = 0; i < SAMPLES; i++){data[sample + 0][i] = 0;}
    // full speed, dual channel
    }else if (rate == 2){
      unsigned long st = millis();
      for (int i = 0; i < SAMPLES; i++){
        data[sample + 0][i] = adRead(ad_ch0, ch0_mode, ch0_off);
        data[sample + 1][i] = adRead(ad_ch1, ch1_mode, ch1_off);
      }
    }
    // .5ms, 1ms or 2ms sampling
    else if (rate >= 3 && rate <= 5){
      const unsigned long r_[] = { 5000 / DOTS_DIV, 10000 / DOTS_DIV, 20000 / DOTS_DIV };
      unsigned long st0 = millis();
      unsigned long st = micros();
      unsigned long r = r_[rate - 3];
      for (int i = 0; i < SAMPLES; i++){
        while ((st - micros()) < r)
          ;
        st += r;
        data[sample + 0][i] = adRead(ad_ch0, ch0_mode, ch0_off);
        data[sample + 1][i] = adRead(ad_ch1, ch1_mode, ch1_off);
      }
    }
    ClearAndDrawGraph();
    CheckSW();
    DrawGrid();
    DrawText();
  }else if (Start){ // 5ms - 500ms sampling
    // copy currently showing data to another
    if (sample == 0){
      for (int i = 0; i < SAMPLES; i++){
        data[2][i] = data[0][i];
        data[3][i] = data[1][i];
      }
    }else{
      for (int i = 0; i < SAMPLES; i++){
        data[0][i] = data[2][i];
        data[1][i] = data[3][i];
      }
    }

    const unsigned long r_[] = { 50000 / DOTS_DIV, 100000 / DOTS_DIV, 200000 / DOTS_DIV,
      500000 / DOTS_DIV, 1000000 / DOTS_DIV, 2000000 / DOTS_DIV,
      5000000 / DOTS_DIV, 10000000 / DOTS_DIV };
    unsigned long st0 = millis();
    unsigned long st = micros();
    for (int i = 0; i < SAMPLES; i++){
      while ((st - micros()) < r_[rate - 6]){
        CheckSW();
        if (rate < 6){break;}
      }
      // sampling rate has been changed
      if (rate < 6){M5.Lcd.fillScreen(BLACK);break;}
      st += r_[rate - 6];
      // sampling rate has been changed to shorter interval
      if (st - micros() > r_[rate - 6]){st = micros();}
      if (!Start){i--;continue;}
      data[sample + 0][i] = adRead(ad_ch0, ch0_mode, ch0_off);
      data[sample + 1][i] = adRead(ad_ch1, ch1_mode, ch1_off);
      ClearAndDrawDot(i);
    }
    DrawGrid();
    DrawText();
  }else{CheckSW();}
  M5.update();
}
//---------------------------------------------------------------------------------------------

M5Stack , Oscilloscope ,

Written by macsbug

4月 3, 2018 @ 11:38 am

カテゴリー: ESP32, M5STACK, Make

4件のフィードバック

Subscribe to comments with RSS.

  1. Hi. i am Botofan Calin the author of the Oscilloscope. I have a bigger (and better) project under development (it include the improved oscilloscope too). If you are interested to test the pre-release version, contact me by e-mail.

    Botofan Calin

    4月 4, 2018 at 4:14 am

    • Thank you for visiting the site.
      It is an honor to have the author see it.
      I was deeply moved by ‘M5Stack-ESP32-Oscilloscope’ and made a case.
      I am interested in the project under development and will contact you by e-mail.
      (google translation)

      macsbug

      4月 4, 2018 at 8:51 am

  2. Good job, is there any OscilloScope PROT Frame related file for download?

    Offer

    4月 4, 2018 at 8:17 am

    • Thank you for visiting the site.
      Wordpress is having trouble not being able to upload data.
      I am not familiar with other methods and are under consideration.

      macsbug

      4月 4, 2018 at 8:58 am


コメントを残す

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

WordPress.com ロゴ

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

Google フォト

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

Twitter 画像

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

Facebook の写真

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

%s と連携中

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