Keiji Fujimoriyamanoshitakairi
Published

add-on scale

This product can add IoT functionality to common weight scale by only putting it.

BeginnerShowcase (no instructions)188
add-on scale

Things used in this project

Hardware components

Spresense boards (main & extension)
Sony Spresense boards (main & extension)
×1
Spresense camera board
Sony Spresense camera board
×1

Software apps and online services

Snappy Ubuntu Core
Snappy Ubuntu Core
Arduino IDE
Arduino IDE
Android Studio
Android Studio
Neural Network Console
Windows 10
Microsoft Windows 10

Hand tools and fabrication machines

Adventure 4

Story

Read more

Schematics

System chart

Move in the direction of the arrow from [boot].

Code

add_on_scale.ino

C/C++
This code to write to Spresense.
#include <Camera.h>
#include <SPI.h>
#include <EEPROM.h>
#include <DNNRT.h>
#include <LowPower.h>
#include "Adafruit_ILI9341.h"

#include <SDHCI.h>
#include <Audio.h>

#include <pthread.h>

SDClass theSD;

AudioClass *theAudio;
int tpre = 0;                     // millis関数を使うので基準値に0を代入しておく

bool bButtonPressed = false;     // bool変数は偽であると定義
int intPin = 4;                  // ストリーミングにするpin
int reset_Pin = 3;               // リセットpinを定義
int flag = 0;                    // camcbの信号をloopに送る変数

err_t err = 0;
String Result_pre = ("0");
String file_value;

/* WiFi */
#define SSID "*************"   //your wifi ssid here 
#define PASS "*************"    //your wifi key here  


#define HOST "1"
#define SENDDATA "SEND"
#define PORT  "333"

#define REPLYBUFFSIZ 0xFFFF
String mozi1 = "",  Receivedata = "", mozi2 = "", connectionID = "";
char replybuffer[REPLYBUFFSIZ];
uint8_t getReply(char *send, uint16_t timeout = 500, boolean echo = true);
uint8_t espreadline(uint16_t timeout = 500, boolean multiline = false);
boolean sendCheckReply(char *send, char *reply, uint16_t timeout = 500);

const uint8_t button15 = PIN_D15;   // sleepを起こすpin

enum {WIFI_ERROR_NONE = 0, WIFI_ERROR_AT, WIFI_ERROR_RST, WIFI_ERROR_SSIDPWD, WIFI_ERROR_SERVER, WIFI_ERROR_UNKNOWN};

/* LCD Settings */                                                        // ディスプレイのピンを定義
#define TFT_RST 8                                                         // リセットを8番ピン
#define TFT_DC  9                                                         // ディジタルを9番ピン
#define TFT_CS  10                                                        // SPI通信のスレーブは10番ピン

// 出力する画像サイズ
#define DNN_IMG_W (28)
#define DNN_IMG_H (28)

//入力する画像サイズ
#define CAM_IMG_W (320)
#define CAM_IMG_H (240)

// クリッピングを開始する開地点と幅と高さ


// 10の位
#define CAM_CLIP_X_1 (0)
#define CAM_CLIP_Y_1 (0)
#define CAM_CLIP_W_1 (112)
#define CAM_CLIP_H_1 (224)
// 1の位
#define CAM_CLIP_X_2 (106)
#define CAM_CLIP_Y_2 (0)
#define CAM_CLIP_W_2 (112)
#define CAM_CLIP_H_2 (224)

// 少数第1
#define CAM_CLIP_X_3 (208)
#define CAM_CLIP_Y_3 (0)
#define CAM_CLIP_W_3 (112)
#define CAM_CLIP_H_3 (224)


int SETFINE = 0;
String index_0;   // 100の位なので「無し」か「1」のどっちか
int index_1;
int index_2;
int index_3;

Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST);     // Adafruit_ILI9341クラスをtftインスタンスにした

uint8_t buf[DNN_IMG_W*DNN_IMG_H];                                           // unit8_tは変数の型:8ビットの非負整数を格納

DNNRT dnnrt;                                                                // DNNRTクラスをdnnrtインスタンスと宣言
                                                                            // 推論させたいデータを渡す
DNNVariable input(DNN_IMG_W*DNN_IMG_H);                                     // 入力データ用のバッファ DNNVariableは独自の変数の型 データ列のメモリを確保 変数を設定
File myFile;               // FileインスタスをmyFileクラスとした  
const char label[11] = {'0','1','2','3','4','5','6','7','8','9',' '};

//********************************************************//
//                ミニTラボくんの星を描画する                  //
//********************************************************//
void mini_drawStar(int x, int y, int outerRadius, int innerRadius, int points, uint16_t color) {
  // 星の各角の角度
  float angle = TWO_PI / points;

  tft.fillCircle(x, y, outerRadius, color);

  for (int i = 0; i < points; i++) {
    float x1 = x + cos(angle * i) * outerRadius;
    float y1 = y + sin(angle * i) * outerRadius;

    float x2 = x + cos(angle * (i + 0.5)) * innerRadius;
    float y2 = y + sin(angle * (i + 0.5)) * innerRadius;

    float x3 = x + cos(angle * (i + 1)) * outerRadius;
    float y3 = y + sin(angle * (i + 1)) * outerRadius;

    tft.fillTriangle(x, y, x1, y1, x2, y2, color);
    tft.fillTriangle(x, y, x2, y2, x3, y3, color);
  }
}

//******************************************************//
//            ミニTラボ君を描画する関数                      //
//******************************************************//
void mini_Tlab(int x, int y, uint16_t color) {
  // xは腕yは顔に合わせている、カラーは顔の色
  
  // y=188のとき足が地面につく
  // x=-40のときちょうど画面外にいる
  // ILI9341_GREEN

  // x座標の中央 = (腕の長さ/2+オブジェクトの長さ/2) -> 顔と胴体
  tft.fillRect  (x+  10, y    , 20,  13, color); // 顔 -> yの基準
  tft.fillCircle(x+  14, y+6.5,  4, ILI9341_WHITE); // 右目
  tft.fillCircle(x+  25, y+6.5,  4, ILI9341_WHITE); // 左目
  tft.fillRect  (x     , y+ 15, 40, 10, 0x8410); // 腕 -> xの基準
  tft.fillRect  (x+13.5, y+ 25, 13, 26, 0x8410); // 胴体

  // 星を描画(x, y, 外側の半径, 内側の半径, 角の数, 色)
  mini_drawStar(x+38, y-3, 2, 5, 5, ILI9341_YELLOW);
}

//******************************************************//
//            ミニTラボ君を消す関数                         //
//******************************************************//
void delete_mini_Tlab(int x, int y) {
  // xは腕yは顔に合わせている、カラーは顔の色
  
  // y=188のとき足が地面につく
  // x=-40のときちょうど画面外にいる
  // ILI9341_GREEN

  // x座標の中央 = (腕の長さ/2+オブジェクトの長さ/2) -> 顔と胴体
  tft.fillRect  (x+  10, y    , 20,  13, ILI9341_BLACK); // 顔 -> yの基準
  tft.fillCircle(x+  14, y+6.5,  4, ILI9341_BLACK); // 右目
  tft.fillCircle(x+  25, y+6.5,  4, ILI9341_BLACK); // 左目
  tft.fillRect  (x     , y+ 15, 40, 10, ILI9341_BLACK); // 腕 -> xの基準
  tft.fillRect  (x+13.5, y+ 25, 13, 26, ILI9341_BLACK); // 胴体

  // 星を描画(x, y, 外側の半径, 内側の半径, 角の数, 色)
  mini_drawStar(x+38, y-3, 2, 5, 5, ILI9341_BLACK);
}


//******************************************************//
//            体重が上がってたときのTラボ                    //
//******************************************************//
void nega_mini_Tlab (int x, int y, uint16_t color) {
  tft.fillRect(x, y-8, 237, 65, ILI9341_BLACK); // 一度Tラボたちを消す

  // 新たなTラボ
  drawRotatedFilledRectangle(x+44, y+25, 13, 26, 0x8410, 60);  // 胴体
  drawRotatedFilledRectangle(x+46, y+12, 40, 10, 0x8410,  60); // 腕
  drawRotatedFilledRectangle(x+65, y+12, 20, 13,  color, 60); // 顔
  tft.fillCircle(x+  61.4, y+18.7,  4, ILI9341_WHITE); // 右目
  tft.fillCircle(x+  67.4, y+  29,  4, ILI9341_WHITE); // 左目
  // 星を描画(x, y, 外側の半径, 内側の半径, 角の数, 色)
  mini_drawStar(x+80, y+47, 2, 5, 5, ILI9341_YELLOW);
}

//******************************************************//
//                      傾きの演出                        //
//******************************************************//
void drawRotatedFilledRectangle(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t color, int16_t angle) {
  // ラジアンに変換
  float radians = angle * (PI / 180.0);

  // 傾けた矩形の各頂点の座標を計算
  int16_t x0 = x;
  int16_t y0 = y;
  int16_t x1 = x0 + width * cos(radians);
  int16_t y1 = y0 + width * sin(radians);
  int16_t x2 = x1 - height * sin(radians);
  int16_t y2 = y1 + height * cos(radians);
  int16_t x3 = x0 - height * sin(radians);
  int16_t y3 = y0 + height * cos(radians);

  // 描画
  tft.fillTriangle(x0, y0, x1, y1, x2, y2, color);
  tft.fillTriangle(x0, y0, x2, y2, x3, y3, color);
}

//***************************************************//
//            右と左を3つの線全部描く関数                 //
//***************************************************//
void spotlight_setting(uint16_t color, int16_t angle) {
  // 左肩上がり(始点x,始点y,終点x,終点y)
  spotlight_draw(320,240,0,0,color, angle);
  spotlight_draw(320,240,0,0,color, angle+3);
  spotlight_draw(320,240,0,0,color, angle-3);
  
  // 右肩上がり
  spotlight_draw(0,240,320,0,color, -angle);
  spotlight_draw(0,240,320,0,color, -(angle+3));
  spotlight_draw(0,240,320,0,color, -(angle-3));
}


//***************************************************//
//            座標変換して線を描く関数                    //
//***************************************************//
void spotlight_draw(int16_t a, int16_t b, int16_t x, int16_t y, uint16_t color, int16_t angle) {
  // 角度をラジアンに
  float radians = angle * (PI / 180.0);
  // (X,Y)座標変換後の座標
  // (x,y)座標変換前の座標
  // (a,b)回転中心
  int16_t X = (x-a)*cos(radians)-(y-b)*sin(radians) + a;
  int16_t Y = (x-a)*sin(radians)+(y-b)*cos(radians) + b;

  tft.drawLine(a, b, X, Y, color);
}

//***************************************************************************//
//                  Tラボ君喜びの演出                                  //
//***************************************************************************//
void while_joy_animattion() {

  // アニメーションに入るまでの時間を取得
  int until_animation_time = millis();
  while(1){

    // 青が動き出す
    delete_mini_Tlab(48,188);
    
    spotlight_setting(ILI9341_CYAN,  0);
    mini_Tlab( 48,168,ILI9341_BLUE);
    mini_Tlab(140,188,ILI9341_RED);
    mini_Tlab(232,188,ILI9341_GREEN);

    digitalWrite(5,HIGH);
    digitalWrite(6,HIGH);
    digitalWrite(7,HIGH);
    
    delay(50);

    // 赤が動き出す
    delete_mini_Tlab( 48,168);
    delete_mini_Tlab(140,188);
    
    mini_Tlab( 48,163,ILI9341_BLUE);
    mini_Tlab(140,168,ILI9341_RED);
    mini_Tlab(232,188,ILI9341_GREEN);

    digitalWrite(5,LOW);
    digitalWrite(6,LOW);
    digitalWrite(7,LOW);
    
    

    delay(50);

    // 緑が動き出す
    delete_mini_Tlab( 48,163);
    delete_mini_Tlab(140,168);
    delete_mini_Tlab(232,188);

    
    mini_Tlab( 48,168,ILI9341_BLUE);
    mini_Tlab(140,163,ILI9341_RED);
    mini_Tlab(232,168,ILI9341_GREEN);

/* LEDつける */
    digitalWrite(5,HIGH);
    digitalWrite(6,HIGH);
    digitalWrite(7,HIGH);
    

    delay(50);
    
    
    // 赤が止まる
    delete_mini_Tlab( 48,168);
    delete_mini_Tlab(140,163);
    delete_mini_Tlab(232,168);

    
    mini_Tlab( 48,188,ILI9341_BLUE);
    mini_Tlab(140,168,ILI9341_RED);
    mini_Tlab(232,163,ILI9341_GREEN);

    digitalWrite(5,LOW);
    digitalWrite(6,LOW);
    digitalWrite(7,LOW);
    
    delay(50);

    // 青が止まる
    delete_mini_Tlab(140,168);
    delete_mini_Tlab(232,163);
    
    mini_Tlab( 48,188,ILI9341_BLUE);
    mini_Tlab(140,188,ILI9341_RED);
    mini_Tlab(232,168,ILI9341_GREEN);
    digitalWrite(5,HIGH);
    digitalWrite(6,HIGH);
    digitalWrite(7,HIGH);
    
    delay(50);

    // 緑が止まる
    delete_mini_Tlab(232,168);
    
    mini_Tlab( 48,188,ILI9341_BLUE);
    mini_Tlab(140,188,ILI9341_RED);
    mini_Tlab(232,188,ILI9341_GREEN);
    digitalWrite(5,LOW);
    digitalWrite(6,LOW);
    digitalWrite(7,LOW);
    
    delay(50);
    

    // 飛んでいる現在の時間を取得
    int animation_time = millis();

    // 差分をとってループを抜ける
    if((animation_time-until_animation_time)>=6800) break;
  }
  digitalWrite(5,HIGH);
  digitalWrite(6,HIGH);
  digitalWrite(7,HIGH);

  delay(1000);
  
  digitalWrite(5,LOW);
  digitalWrite(6,LOW);
  digitalWrite(7,LOW);
  spotlight_setting(ILI9341_CYAN,  0);
  mini_Tlab( 48,188,ILI9341_BLUE);
  mini_Tlab(140,188,ILI9341_RED);
  mini_Tlab(232,188,ILI9341_GREEN);
}

  

//******************************************//
//         ループからの音楽再生              //
//******************************************//
void sound_play_joy(String name, int volume)
{
  err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/sd0/BIN", AS_SAMPLINGRATE_AUTO, AS_CHANNEL_STEREO);
  myFile = theSD.open(name); 
  err = theAudio->writeFrames(AudioClass::Player0, myFile);   // SDカードからデータを読み込んでファイルに入れる
  theAudio->setVolume(volume);
  theAudio->startPlayer(AudioClass::Player0);                 // SpresenseのFIFOの中のデータを引っ張って再生

  // アニメーションに必要な変数たち
  // struct joy_settings parametor;
  // parametor.jump_cnt = 0;
  // parametor.stand_cnt = 0;
  // parametor.jump_animation = false;

  // ディスプレイをマルチスレッドで処理
  pthread_t display;
  
  pthread_create(&display, NULL, while_joy_animattion, NULL);
  
  while(1){
    // joy_animation(jump_cnt,stand_cnt,jump_animation);
    err = theAudio->writeFrames(AudioClass::Player0, myFile);      // SDカードからデータを読み込んでファイルに入れる
    if (err){
      printf("Main player error code: %d\n", err);
      theAudio->stopPlayer(AudioClass::Player0);    // オーディオをストップ
      break;
      }
  }
}

//***************************************************************************//
//                     セットアップ関数からのオーディオ処理                         //
//***************************************************************************//
void audio_setup(String track, int volume) {
  /* オーディオ処理 */
  theAudio = AudioClass::getInstance(); 
  theAudio->begin();                                                     // オーディオを初期化してaudio_attention_cbを呼び出す()の中は空欄でもいいらしい
  theAudio->setRenderingClockMode(AS_CLKMODE_NORMAL);                    // クロックモードの設定、AS_CLKMODE_NORMAL:49Hz以下とAS_CLKMODE_HIRES:90Hz異常の2つの設定がある
  theAudio->setPlayerMode(AS_SETPLAYER_OUTPUTDEVICE_SPHP, AS_SP_DRV_MODE_LINEOUT);           // 出力先:スピーカ出力に設定、出力サイズ:スピーカ出力をラインアウトレベル設定(ジャックを使うならこれ)
  theAudio->setVolume(volume);
  err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/sd0/BIN", AS_SAMPLINGRATE_AUTO, AS_CHANNEL_STEREO); 
  /* 1曲目 */
  myFile = theSD.open(track);         // ファイルを開く
  puts("file open");                         // ファイルのオープンを知らせる
  err = theAudio->writeFrames(AudioClass::Player0, myFile);   // とりあえずなんかFIFOに入れておく
  theAudio->startPlayer(AudioClass::Player0);                 // オーディオスタート
}

//********************************************************//
//                Tラボくんの星を描画する                     //
//********************************************************//
void drawStar(int x, int y, int outerRadius, int innerRadius, int points, uint16_t color) {
  // 星の各角の角度
  float angle = TWO_PI / points;

  tft.fillCircle(x, y, outerRadius, color);

  for (int i = 0; i < points; i++) {
    float x1 = x + cos(angle * i) * outerRadius;
    float y1 = y + sin(angle * i) * outerRadius;

    float x2 = x + cos(angle * (i + 0.5)) * innerRadius;
    float y2 = y + sin(angle * (i + 0.5)) * innerRadius;

    float x3 = x + cos(angle * (i + 1)) * outerRadius;
    float y3 = y + sin(angle * (i + 1)) * outerRadius;

    tft.fillTriangle(x, y, x1, y1, x2, y2, color);
    tft.fillTriangle(x, y, x2, y2, x3, y3, color);
  }
}

//******************************************************//
//            Tラボ君を描画する関数                         //
//******************************************************//
void Tlab() {
  tft.fillScreen(ILI9341_BLACK);
  tft.fillRect(100,  86, 120, 30, 0x8410); // 腕
  tft.fillRect(140, 116,  40, 80, 0x8410); // 胴体
  tft.fillRect(130,  44,  60, 40, ILI9341_GREEN); // 顔
  tft.fillCircle(145, 64, 12, ILI9341_WHITE); // 目
  tft.fillCircle(175, 64, 12, ILI9341_WHITE); // 目
  // 星を描画(x, y, 外側の半径, 内側の半径, 角の数, 色)
  drawStar(210, 30, 8, 16, 5, ILI9341_YELLOW);   // 星
}

//***************************************************************************//
//                     値が確定する前のディスプレイ設定                            //
//***************************************************************************//
void display_setup() {
  tft.begin();                                     // ディスプレイを初期化
  tft.setRotation(3);                              // ディスプレイの向きを3番にする、1番と検討
  tft.fillRect(0,0, 320, 240, ILI9341_BLACK);      // 背景の色
  tft.setTextSize(8);                             // テキストのサイズ
  tft.setCursor(40, 88);                          // setCursor(col.row) col:列 row:行
  tft.setTextColor(ILI9341_YELLOW);                // テキストの色
  // Tlab君の描画
  Tlab();
  // tft.println("? ? ?");                                // ディスプレイに文字型を出力
}

//***************************************************************************//
//                         スリープ関数の定義                              //
//***************************************************************************//
void lowpower() {
  /* LowPowerライブラリを初期化*/
  LowPower.begin();
  bootcause_e bc = LowPower.bootCause();           // boot causeを取得
  printBootCause(bc);                              // 引数をbcとして関数にとぶ
  pinMode(button15, INPUT_PULLUP);                 // sleepを起こすpin
  attachInterrupt(button15, pushed15, FALLING);    // sleepを起こすには割り込み関数が必要
  LowPower.enableBootCause(button15);              // 起こすのに15ピンは有効

  // pinMode(intPin, INPUT_PULLUP);                 // sleepを起こすpin
  // attachInterrupt(intPin, pushed15, FALLING);    // sleepを起こすには割り込み関数が必要
  // LowPower.enableBootCause(intPin);              // 起こすのに5ピンは有効
}

//***************************************************************************//
//                     ファイルから前回の値を読み込む関数                           //
//***************************************************************************//
void read_file() {
  while (!theSD.begin()) { putStringOnLcd("Insert SD card", ILI9341_RED); } // エラーの処理、putStringOnLcd関数に引数Insert SD cardを赤文字で返して出力
  myFile = theSD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");

    /* ファイルを読み取る */
    while (myFile.available()) {              // 1文字ずつ読み取る
      file_value += char(myFile.read());       // 読み取る関数:前回起動時の値を読み取る
    }
    Serial.println(file_value);               // この時点で文字型
    myFile.close();                           // いったんファイル閉じる
  } else {
    Serial.println("error opening test.txt"); // エラー処理
  }
}


//***************************************************************************//
//                          AIとカメラの設定と処理                               //
//***************************************************************************//
void cam_AI() {
  File nnbfile = theSD.open("model.nnb");     // 学習済みデータの読み込み

  /* AIの処理 */
  int ret = dnnrt.begin(nnbfile);             // DNNRT(組み込みAIライブラリ)を学習済みモデルで初期化(ランタイムライブラリ)
  if (ret < 0) {
    putStringOnLcd("dnnrt.begin failed" + String(ret), ILI9341_RED);     // エラーの処理、もしmodel.nnbファイルがなかったら初期化失敗と表示
    return;
  }
  
  /* カメラ処理 */
  theCamera.begin();                         // 液晶ディスプレイの開始 
  theCamera.startStreaming(true, CamCB);     // カメラのストリーミング開始CamCBに返す認識処理もCamCBで行っている
}

//***************************************************************************//
//                  changeState関数:bButtonPressedを真にする                   //
//***************************************************************************//
void changeState () {
  if(bButtonPressed == false){
    bButtonPressed = true;
    puts("true");
    }else{
      bButtonPressed = false;
      puts("false");
      // 画面を消す
      tft.fillScreen(ILI9341_BLACK);
      theCamera.startStreaming(false, CamCB);  // ストリーミングを終了
      // スリープモードになる
      lowpower();
      }
}


//****************************************************************************//
//                        クリップサイズと推論実行を担う関数                        //
//****************************************************************************//
int Number_Re(CamImage img, int O_X, int O_Y) {
  CamImage small;

  
  CamErr err = img.clipAndResizeImageByHW(small
                     , O_X, O_Y
                     , O_X + CAM_CLIP_W_1 -1
                     , O_Y + CAM_CLIP_H_1 -1
                     , DNN_IMG_W, DNN_IMG_H);
               

  if (!small.isAvailable()){
    putStringOnLcd("Clip and Reize Error:" + String(err), ILI9341_RED);
    return;
  }

  // 認識用モノクロ画像を設定
  small.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);                              // ニューラルコンソールの画像がモノラルだったのでここでモノラル化する 便宜的にRGB565に変換 yuvでもいいらしい
  uint16_t* tmp = (uint16_t*)small.getImgBuff();

  float *dnnbuf = input.data();                                                  // input.data()で中身のポインタを取得
  float f_max = 0.0;                                                             // 最大値を定義
                                                                                 // 入力用画像データを認識用入力データにコピー  
  for (int n = 0; n < DNN_IMG_H*DNN_IMG_W; ++n) {                                // RGB→5bit,6bit,5bitとなっている 今回はGの6bitでビットマスクをかけてGだけの値を出すようにした0~64の値
    dnnbuf[n] = (float)((tmp[n] & 0x07E0) >> 5);
    if (dnnbuf[n] > f_max) f_max = dnnbuf[n];                                    // 前処理、画像のなかの最大値を定義
  }
  for (int n = 0; n < DNN_IMG_W*DNN_IMG_H; ++n) {
    dnnbuf[n] /= f_max;                                                           // 最大値で割って正規化
  }
  
  // 推論の実行
  dnnrt.inputVariable(input, 0);
  dnnrt.forward();
  DNNVariable output = dnnrt.outputVariable(0);
  int index = output.maxIndex();

  return index;     // 返す値
}


//**************************************************************************************************************************************************************//
//                                                   文字の下の黒いところを処理する関数                                                                              //
//**************************************************************************************************************************************************************//
void putStringOnLcd(String str, int color) {
  int len = str.length();                                                    // 整数lenを引数strの文字数として代入
  tft.fillRect(0,224, 320, 240, ILI9341_BLACK);                              // fillReact(x:左上からのx座標,y:左上からのy座標,w:幅,h:高さ,色)
  tft.setTextSize(2);                                                        // テキストの文字の大きさ
  int sx = 160 - len/2*12;                                                   // 文字の始まる列を計算によってもとめている
  if (sx < 0) sx = 0;                                                        // もしマイナスを取ることがあったらそれは0と見ていい
  tft.setCursor(sx, 225);                                                    // setCursor(col.row) col:列 row:行
  tft.setTextColor(color);                                                   // テキストの色を引数であるcolorにする
  tft.println(str);                                                          // ディスプレイに文字型を出力
}


//****************************************************************************************************************//
//                                  赤枠の関数                                                                      //
//****************************************************************************************************************//
void drawBox(uint16_t* imgBuf, int offset_x, int offset_y, int width, int height, int thickness, int color) {
  /* Draw target line */                                                     // 目的の線を引く
  for (int x = offset_x; x < offset_x+width; ++x) {                          // for文で上で定義したクリップ枠線を引いていく(x座標)
    for (int n = 0; n < thickness; ++n) {
      *(imgBuf + CAM_IMG_W*(offset_y+n) + x)           = color;
      *(imgBuf + CAM_IMG_W*(offset_y+height-1-n) + x) = color;
    }
  }
  for (int y = offset_y; y < offset_y+height; ++y) {                         // for文で上で定義したクリップ枠線を引いていく(y座標) 
    for (int n = 0; n < thickness; ++n) {                                    // ラインの太さLINE_THICKNESSは上記で定義している for文で繰り返す
      *(imgBuf + CAM_IMG_W*y + offset_x+n)           = color;                // CamCBからのimgBufを受け取る
      *(imgBuf + CAM_IMG_W*y + offset_x + width-1-n) = color;
    }
  }  
}


//*****************************************************************************************************************************//
//                                            ビデオ画像と学習済みデータを習得するためのコールバック関数                                  //
//******************************************************************************************************************************//
void CamCB(CamImage img) {
  /* ストリーミング可能かどうかの処理 */
  if (!img.isAvailable()) {
    Serial.println("Image is not available. Try again");
    return;
  }

  
  /* カメラ画像の切り抜きと縮小 */
  index_0 = "";
  index_1 = Number_Re(img, CAM_CLIP_X_1, CAM_CLIP_Y_1);
  index_2 = Number_Re(img, CAM_CLIP_X_2, CAM_CLIP_Y_2);
  index_3 = Number_Re(img, CAM_CLIP_X_3, CAM_CLIP_Y_3);

  // 10の位が0だった場合は100の「位に1がたつ
  // 109.9kg までの測定が可能
  if(index_1 == 0) {
    index_0 = "1";
  }

  /* 変数の文字列を定義 */
  String gStrResult = index_0 + String(label[index_1]) + String(label[index_2]) + String(".") + String(label[index_3]);
  
  img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);    // ディスプレイに出力するためにRGB565に変換
  uint16_t* imgBuf = (uint16_t*)img.getImgBuff();    // 毎回枠を書かないようにimgBufをつくる getImgBuff()で画像データのバッファアドレスを取得
 
  /* drawBox関数で座標や色を指定、赤い枠を書く */
  // drawBox(imgBuf, CAM_CLIP_X,   CAM_CLIP_Y,   CAM_CLIP_W,   CAM_CLIP_H,   5, ILI9341_RED); 
  drawBox(imgBuf, CAM_CLIP_X_1, CAM_CLIP_Y_1, CAM_CLIP_W_1, CAM_CLIP_H_1, 5, ILI9341_RED); 
  drawBox(imgBuf, CAM_CLIP_X_2, CAM_CLIP_Y_2, CAM_CLIP_W_2, CAM_CLIP_H_2, 5, ILI9341_RED); 
  drawBox(imgBuf, CAM_CLIP_X_3, CAM_CLIP_Y_3, CAM_CLIP_W_3, CAM_CLIP_H_3, 5, ILI9341_RED);

  /* 4ピンを押すとストリーミングが始まる */
  if(bButtonPressed == false){
    puts("?");

    int t = millis();      // 秒数を取っていく

    /* 秒数の差を出し続ける、条件を越えたら次の条件式に移る */
    if((t-tpre)>500 && t>=5000 && gStrResult.toFloat() != 0.0 ){
      Serial.println(gStrResult.toFloat());

      /* 前のデータと後のデータが同じになれば変数を変えてloopに渡し、カメラ終了 */
      if(Result_pre == gStrResult){ 
        flag = 1;                                // loop関数に渡す
        Serial.println(t);
        theCamera.startStreaming(false, CamCB);  // ストリーミングを終了
        
      }else{
        Serial.println("one more time");         // 達成しなかったらもう一回と表示
        }
        tpre = t;                                // 基準値を更新
        Result_pre = gStrResult;                 // Result_preにgStrResultの文字列を格納
        
  }
 }else{
  /* bButtonPressedがtrueならストリーミングとしての処理 */
  flag = 2;     // 曲を停止するためにflagに2を返す
  tft.drawRGBBitmap(0, 0, (uint16_t *)img.getImgBuff(), 320, 224);           // 書いた画像をそのまま転送
  gStrResult = String(label[index_1]) + String(label[index_2]) + String(".") + String(label[index_3]);
  putStringOnLcd(gStrResult, ILI9341_YELLOW);                                // 結果を出力する
 }
}

//******************************************************************//
//                        リセット関数                                //
//******************************************************************//
void software_reset(){
  digitalWrite(reset_Pin, LOW);
}

//******************************************************************//
//                     引数を与えてサーバーになる関数                    //
//******************************************************************//
boolean ESP_GETpage(char *host, uint16_t port, String sdata) {
  String cmd = "AT+CIPSERVER=";   // サーバーになる
  cmd += host;
  cmd += ",";
  cmd += port;
  cmd.toCharArray(replybuffer, REPLYBUFFSIZ);

  getReply(replybuffer);

  if (strcmp(replybuffer, "OK") != 0) {
    while (true) {
      espreadline(500);  // this is the 'echo' from the data
      Serial.print("<--- "); Serial.println(replybuffer);
      //split for Receivedata
      mozi1 = String(strtok(replybuffer, ","));
      connectionID = String(strtok(NULL, ","));
      mozi1 = String(strtok(NULL, ":"));
      Receivedata = String(strtok(NULL, ":"));
      mozi2 = String(strtok(NULL, ":"));
      //Serial.println(mozi1); Serial.println(mozi2);

      if (mozi1 != "") {
        Serial.print("Connection ID = "); Serial.println(connectionID);
        Serial.print("Receivedata = "); Serial.println(Receivedata);
      }

      if (strstr(replybuffer, "+IPD"))
        break;
    }
  }

  delay(500);
  String request = "";
  request += sdata;
  cmd = "AT+CIPSEND=";   // データの送信
  cmd += connectionID;
  cmd += ",";
  cmd += 2 + request.length();                      // 必要な情報量に達するために2足した
  cmd.toCharArray(replybuffer, REPLYBUFFSIZ);
  sendCheckReply(replybuffer, ">");

  Serial.print("Sending: "); Serial.println(request.length());
  Serial.println(F("*********SENDING*********"));
  Serial.println(request);
  Serial.println(F("*************************"));
  request.toCharArray(replybuffer, REPLYBUFFSIZ);
  Serial2.println(request);

  while (true) {
    espreadline(3000);  // this is the 'echo' from the data
    Serial.print(">"); Serial.println(replybuffer); // probably the 'busy s...'

    if (strstr(replybuffer, "wrong syntax"))
      continue;
    else if (strstr(replybuffer, "ERROR"))
      continue;
    else if (strstr(replybuffer, "busy s..."))
      continue;
    else if (strstr(replybuffer, "OK"))
      break;
    else break;
  }

  if (! strstr(replybuffer, "SEND OK") ) return false;

}

//*************************************************************//
//                   WiFiをリセットさせる関数                      //
//*************************************************************//
boolean espReset() {
  getReply("AT+RST", 1000, true);  // リセット
  if (! strstr(replybuffer, "OK")) return false;
  delay(500);
  // turn off echo
  getReply("ATE0", 250, true);
  return true;
}

//*******************************************************************//
//                      WiFiにつなげる関数                             //
//*******************************************************************//
boolean ESPconnectAP(char *s, char *p) {

  getReply("AT+CWMODE=1", 500, true);  // ステーションモードに設定
  if (! (strstr(replybuffer, "OK") || strstr(replybuffer, "no change")) )
    return false;

  String connectStr = "AT+CWJAP=\"";   // ネットに接続
  connectStr += SSID;
  connectStr += "\",\"";
  connectStr += PASS;
  connectStr += "\"";
  connectStr.toCharArray(replybuffer, REPLYBUFFSIZ);
  getReply(replybuffer, 200, true);

  while (true) {
    espreadline(200);  // this is the 'echo' from the data
    if ((String)replybuffer == "") {
      Serial.print(".");
    } else {
      Serial.println("");
      Serial.print("<--- "); Serial.println(replybuffer);
    }
    if (strstr(replybuffer, "OK"))
      break;
  }
  return true;
}

//**********************************************************************//
//                    WiFiをリセットする関数                               //
//**********************************************************************//
byte setupWiFi() {
  // reset WiFi module
  Serial.println(F("Soft resetting..."));
  if (!espReset())
    return WIFI_ERROR_RST;

  delay(500);

  if (!ESPconnectAP(SSID, PASS))
    return WIFI_ERROR_SSIDPWD;

  Serial.println(F("Multiple Mode"));

  if (!sendCheckReply("AT+CIPMUX=1", "OK"))   // マルチプルコネクションモード
    return WIFI_ERROR_SERVER;

  return WIFI_ERROR_NONE;
}

//************************************************************************//
//                      IPアドレスを取得する関数                              //
//************************************************************************//
boolean getIP() {
  getReply("AT+CIFSR", 100, true);            // IPアドレスの取得
  while (true) {
    espreadline(50);  // this is the 'echo' from the data
    Serial.print("<--- "); Serial.println(replybuffer);
    if (strstr(replybuffer, "OK"))
      break;
  }

  delay(100);

  return true;
}

//*************************************************************************//
//                      ポータから読んだ行数を返す                              //
//*************************************************************************//
uint8_t espreadline(uint16_t timeout, boolean multiline) {
  uint16_t replyidx = 0;

  while (timeout--) {
    if (replyidx > REPLYBUFFSIZ - 1) break;

    while (Serial2.available()) {
      char c =  Serial2.read();
      if (c == '\r') continue;
      if (c == 0xA) {
        if (replyidx == 0)   // the first 0x0A is ignored
          continue;

        if (!multiline) {
          timeout = 0;       // the second 0x0A is the end of the line
          break;
        }
      }
      replybuffer[replyidx] = c;
      replyidx++;
    }

    if (timeout == 0) break;
    delay(1);
  }
  replybuffer[replyidx] = 0;  // null term
  return replyidx;
}

//************************************************************************//
//                           読んだ行の文字数を返す関数                        //
//************************************************************************//
uint8_t getReply(char *send, uint16_t timeout, boolean echo) {
  // フラッシュ入力
  while (Serial2.available()) {
    Serial2.read();
    delay(1);
  }

  if (echo) {
    Serial.print("---> "); 
    Serial.println(send);
  }
  Serial2.println(send);

  // 最初の返信文を食べる (エコー)
  uint8_t readlen = espreadline(timeout);

  if (strncmp(send, replybuffer, readlen) == 0) {
    // そのエコー、別の行を読んでください!
    readlen = espreadline();
  }

  if (echo) {
    Serial.print ("<--- "); Serial.println(replybuffer);
  }
  return readlen;
}

boolean sendCheckReply(char *send, char *reply, uint16_t timeout) {

  getReply(send, timeout, true);

  return (strcmp(replybuffer, reply) == 0);
}

//**********************************************************************************//
//                           エラー内容を表示する関数                                   //
//**********************************************************************************//
void debugLoop() {
  Serial.println("========================");
  //diag のシリアル ループ モード
  while (1) {
    if (Serial.available()) {
      Serial2.write(Serial.read());
    }
    if (Serial2.available()) {
      Serial.write(Serial2.read());
    }
  }
}

//***********************************************************************************//
//                             何番pinで押したかわかる関数                               //
//***********************************************************************************//
void printBootCause(bootcause_e bc)
{
  Serial.println("--------------------------------------------------");
  Serial.print("Boot Cause: ");
  if ((COLD_GPIO_IRQ36 <= bc) && (bc <= COLD_GPIO_IRQ47)) {
    // Wakeup by GPIO
    int pin = LowPower.getWakeupPin(bc);
    Serial.print(" <- pin ");
    Serial.print(pin);
  }
  Serial.println();
  Serial.println("--------------------------------------------------");
}

//******************************************//
//             初期化する割り込み               //
//******************************************//
void pushed15()
{
  Serial.println("Pushed D15!");
  software_reset();
}



//****************************************************ループで使われている関数**********************************************************//

//******************************************//
//        体重未確定時のスロットの演出          //
//******************************************//
void slot_count_tft() {
  tft.fillRect(0,0, 320, 240, ILI9341_BLACK);
  tft.setCursor(40, 85);
  tft.setTextSize(10);
  tft.setTextColor(ILI9341_YELLOW);
  tft.print(random(0, 9));
  tft.print(random(0, 9));
  tft.print(".");
  tft.print(random(0, 9)); 
}

//******************************************//
//         ループからの音楽再生              //
//******************************************//
void sound_play(String name, int volume)
{
  err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/sd0/BIN", AS_SAMPLINGRATE_AUTO, AS_CHANNEL_STEREO);
  myFile = theSD.open(name); 
  err = theAudio->writeFrames(AudioClass::Player0, myFile);   // SDカードからデータを読み込んでファイルに入れる
  theAudio->setVolume(volume);
  theAudio->startPlayer(AudioClass::Player0);                 // SpresenseのFIFOの中のデータを引っ張って再生

  while(1){
    err = theAudio->writeFrames(AudioClass::Player0, myFile);      // SDカードからデータを読み込んでファイルに入れる
    if (err){
      printf("Main player error code: %d\n", err);
      theAudio->stopPlayer(AudioClass::Player0);    // オーディオをストップ

      break;
    }
  }     
}

//******************************************//
//       差分の計算とディスプレイの出力         //
//******************************************//
void seek_difference_tft(float& file_value_flo,float& difference, float& Result_pre_flo) {
  
  // 確定した値の出力
  tft.fillRect(0,0, 320, 240, ILI9341_BLACK);
  tft.setTextSize(9);
  int len = Result_pre.length();
  int sx  = 160 - len/2*6*10;
...

This file has been truncated, please download it to see its full contents.

Credits

Keiji Fujimori

Keiji Fujimori

1 project • 1 follower
I'm graduate student. I major fine mechanics. I want to provide you to Impressed and laughter by developing.
yamanoshitakairi

yamanoshitakairi

0 projects • 1 follower

Comments