/* 圧力トレンドロガー(_20190311DataTrendLogger.ino) 0.96インチOLEDにアナログ入力の変化グラフを書く (表示フォーマットや換算式をある程度汎用化したバージョン) 2019/03/11 ラジオペンチ http://radiopench.blog96.fc2.com/ デモ用にmemCheck,memPrintを追加 */ #include #include // adafruitのライブラリを使用 #include #include // 定周期割込みに使用 #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define STRING_L 4 // 合計表示文字数 #define AFTER_DECIMAL_L 0 // 小数点以下の表示桁数 #define LOG_INTERVAL 100 // ログ記録周期 100ms // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); int latestData; int dataBuff[100]; // データーバッファ char chrBuff[10]; // 表示フォーマットバッファ int dataMin; int dataMax; volatile boolean timeFlag = LOW; float aaa = 4.5332; // 圧力換算係数 aaa y =aaa * x + bbb float bbb = -448.0; //        bbb // メモリチェック用変数(ここから) int aRamStart = 0x0100; // RAM先頭アドレス(固定値) int aGvalEnd; // グローバル変数領域末尾アドレス int aHeapEnd; // ヒープ領域末尾アドレス(次のヒープ用アドレス) int aSp; // スタックポインタアドレス(次のスタック用アドレス) char aBuff[6]; // 表示フォーマット操作バッファ void setup() { pinMode(13, OUTPUT); Serial.begin(115200); checkMem(); // メモリーの使用状況をチェック printMem(); // チェック結果をシリアルに表示 if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64 Serial.println(F("SSD1306 allocation failed")); for (;;); // Don't proceed, loop forever } checkMem(); // メモリーの使用状況をチェック printMem(); // チェック結果をシリアルに表示 for (int i = 0; i <= 99; i++) { dataBuff[i] = -1; // バッファを未定義フラグ(-1)で埋める } latestData = pressMes(); // 読み捨て latestData = pressMes(); display.clearDisplay(); // 画面全消去(0.4ms) writeCommonImage(); // 共通部分を描画 dispNewData(); // 最新値を表示(1.42ms) display.display(); MsTimer2::set(LOG_INTERVAL, timeUp); // 指定インターバルで割り込み MsTimer2::start(); // タイマー割り込み開始 } void loop() { digitalWrite(13, HIGH); latestData = pressMes(); // 圧力測定(26ms) saveBuff(); // 表示バッファに書き込み(180us) writeCommonImage(); // 共通イメージ作画(5.9ms) dispNewData(); // 最新の値を表示(1.42ms) plotData(); // グラフプロット(約20ms波形によって変わる) dispVscale(); // 縦軸目盛り表示(6.6ms) display.display(); // バッファの値を転送して表示(37ms) digitalWrite(13, LOW); // 処理時間合計=約95ms while (timeFlag == LOW) { // MsTimer2割込みが入るまで待つ } timeFlag = LOW; // フラグをクリアして次の割込みに備える } int pressMes() { // 圧力測定ルーチン int pD; long x = 0; int count = 200; // 平均回数 for (int i = 0; i < count; i++) { // 指定回数積分 x = x + analogRead(0); // 圧力センサーを読む } pD = x / (count / 20); // 平均値計算(但し20倍) // Serial.println(mmAq(pD)); return pD; // 圧力センサーの値を返す } void dispVscale() { // 縦軸目盛りの値を表示 float p1, p2, delta; p1 = mmAq(dataMin); // 圧力値に変換 p2 = mmAq(dataMax); // delta = p2 - p1; dtostrf(p2, STRING_L, AFTER_DECIMAL_L, chrBuff); // 書式設定 display.setCursor(0, 9); display.print(chrBuff); // Max値表示 dtostrf(p1 + 2.0 * delta / 3.0, STRING_L, AFTER_DECIMAL_L, chrBuff); // 書式設定 display.setCursor(0, 24); display.print(chrBuff); // 2/3値表示 dtostrf(p1 + delta / 3.0, STRING_L, AFTER_DECIMAL_L, chrBuff); // 書式設定 display.setCursor(0, 41); display.print(chrBuff); // 1/3値表示 dtostrf(p1, STRING_L, AFTER_DECIMAL_L, chrBuff); // 書式設定 display.setCursor(0, 57); display.print(chrBuff); // Min値表示 } void saveBuff() { // データバッファの更新と最大・最小値の修正 int d; dataMin = 30000; // 最小値 dataMax = 0; // 最大値 for (int i = 98; i >= 0; i--) { // 配列に値を保存しながら最大と最小値を求める d = dataBuff[i]; dataBuff[i + 1] = d; // 配列のデーターを一つ後ろにずらし if (d != -1) { // ずらしたデータが有効値だったら、 if (d < dataMin) { // 最小と dataMin = d; } if (d > dataMax) { // 最大値を記録 dataMax = d; } } } dataBuff[0] = latestData; // 配列の先頭には最新データーを記録し、 if (latestData < dataMin) { // 最小と dataMin = latestData; } if (latestData > dataMax) { // 最大値を再確認 dataMax = latestData; } } void dispNewData() { // 最新データーの値を画面の右上に表示 float p; p = mmAq(latestData); dtostrf(p, STRING_L, AFTER_DECIMAL_L, chrBuff); // 指定フォーマットで display.setCursor(80, 0); display.print(chrBuff); // 最新測定値を表示 } void plotData() { // 配列の値に基づきデーターをプロット long y1, y2; for (int i = 1; i <= 99; i++) { // 線で接続するために、先頭の次データから開始 if (dataBuff[i] == -1) { // データーが未定(-1)なら break; // プロット中止 } y1 = map(dataBuff[i - 1], dataMin, dataMax, 63, 9); // y1をプロット座標へ変換(i番目のデーターが有効なのでその前は必ず有効) y2 = map(dataBuff[i], dataMin, dataMax, 63, 9); // y2もプロット座標へ変換 display.drawLine(127 - i, y1, 126 - i, y2, WHITE); // 点間を線で結ぶ } } float mmAq(int x) { // データーをmmAq単位の気圧に換算 float y; y = x * aaa / 20.0 + bbb; // 圧力計算式(xは20倍で保存されているので、ここで補正) return y; } void writeCommonImage() { // 共通画面の作画 display.clearDisplay(); // 画面全消去(0.4ms) display.setTextColor(WHITE); // 白文字で描く display.setCursor(0, 0); // 画面左上から、 display.println(F("Press 30Hr")); // グラフタイトル表示 display.setCursor(104, 0); // 右上に、 display.println(F("mmAq")); // 単位記号 display.drawFastVLine(26, 9, 55, WHITE); // 左縦線 display.drawFastVLine(127, 9, 55, WHITE); // 右縦線 display.drawFastHLine(25, 9, 3, WHITE); // 左端、Max値の補助マーク display.drawFastHLine(25, 63, 3, WHITE); display.drawFastHLine(46, 9, 3, WHITE); // 中間、Max値の補助マーク display.drawFastHLine(46, 63, 3, WHITE); display.drawFastHLine(66, 9, 3, WHITE); // 中間、Max値の補助マーク display.drawFastHLine(66, 63, 3, WHITE); display.drawFastHLine(86, 9, 3, WHITE); // 中間、Max値の補助マーク display.drawFastHLine(86, 63, 3, WHITE); display.drawFastHLine(106, 9, 3, WHITE); // 中間、Max値の補助マーク display.drawFastHLine(106, 63, 3, WHITE); display.drawFastHLine(126, 9, 2, WHITE); // 右端、Max値の補助マーク display.drawFastHLine(126, 63, 2, WHITE); for (int y = 26; y < 60; y += 19) { // 水平目盛り線を点線で2本書く for (int x = 25; x <= 128; x += 4) { display.drawFastHLine(x, y, 2, WHITE); } } for (int x = (127 - 20); x > 30; x -= 20) { // 縦目盛り線を点線で4本描く for (int y = 10; y < 63; y += 5) { display.drawFastVLine(x, y, 2, WHITE); } } } //-------メモリ使用状況表示------------------------------------------------- // ここから↓ void printMem() { // RAM使用状況を表示 Serial.println(); Serial.println(F("RAM allocation table")); Serial.println(F("usage start end size")); Serial.print(F("groval: ")); // 固定アドレス printHexDecimal(aRamStart); // 開始 Serial.print(F(" - ")); printHexDecimal(aGvalEnd); // 終了 Serial.print(" "); printHexDecimal(aGvalEnd - aRamStart + 1); // サイズ Serial.println(); Serial.print(F("heap : ")); // ヒープ printHexDecimal(aGvalEnd + 1); // 開始 Serial.print(F(" - ")); printHexDecimal(aHeapEnd - 1); // 終了(aHeapEndは次のヒープ用のアドレスなので-1) Serial.print(F(" ")); printHexDecimal(aHeapEnd - 1 - (aGvalEnd + 1) + 1); // サイズ Serial.println(); Serial.print(F("free : ")); // Free領域 printHexDecimal(aHeapEnd); // 開始 Serial.print(F(" - ")); printHexDecimal(aSp); // 終了 Serial.print(F(" ")); printHexDecimal(aSp - aHeapEnd + 1); // サイズ Serial.println(); Serial.print(F("stack : ")); // スタック領域 printHexDecimal(aSp + 1); // 開始(aSpは次のスタック用アドレスなので+1) Serial.print(F(" - ")); printHexDecimal(RAMEND); // 終了(RAMENDは0x8fffでシステム側で定義) Serial.print(F(" ")); printHexDecimal(RAMEND - (aSp + 1) + 1); // サイズ Serial.println(); Serial.println(); } void checkMem() { // RAM使用状況を記録 // 意味不明なところもあるが、そのまま使用 uint8_t *heapptr, *stackptr; stackptr = (uint8_t *)malloc(4); // とりあえず4バイト確保 heapptr = stackptr; // save value of heap pointer free(stackptr); // 確保したメモリを返却 stackptr = (uint8_t *)(SP); // SPの値を保存(SPには次のスタック用の値が入っている) aSp = (int)stackptr; // スタックポインタの値を記録 aHeapEnd = (int)heapptr; // ヒープポインタの値を記録 aGvalEnd = (int)__malloc_heap_start - 1; // グローバル変数領域の末尾アドレスを記録 } void printHexDecimal(int x) { // 引数を16進と10進で表示 0xHHHH(dddd) sprintf(aBuff, "%04X", x); // 16進4桁に変換 Serial.print(F("0x")); Serial.print(aBuff); Serial.print(F("(")); sprintf(aBuff, "%4d", x); // 10進4桁に変換 Serial.print(aBuff); Serial.print(F(")")); } // ここまで↑ コピペ void timeUp() { // MsTimer2割込み処理 timeFlag = HIGH; }