nao_IoTのアイコン画像
nao_IoT 2022年09月24日作成 (2022年09月25日更新)
製作品 製作品 閲覧数 351
nao_IoT 2022年09月24日作成 (2022年09月25日更新) 製作品 製作品 閲覧数 351

Light Recorder【照明のON/OFFを検知して記録!】

Light Recorder【照明のON/OFFを検知して記録!】

概要

Spresenseを使って、部屋の照明の点灯状態を記録するデバイスを作成しました!留守番・一人暮らしの見守り用途や、部屋の電気を消したか気になる人向けのチェッカーとして応用できると思います。

※IoT化したかったもののWi-Fi接続用のボードを用意していなかったため、ネットワークには接続していません。

デモ動画

設定編(時間設定・ISO感度設定)

ここに動画が表示されます

照明状態を記録していく様子

ここに動画が表示されます

使用部品

Spresenseメインボード x1
Spresense拡張ボード x1
Spresenseカメラモジュール x1

LCD I2Cシリアル接続 2x16キャラクタ x1

2連タクトスイッチ(古い掃除機から取り出したもの) x1
10kΩ炭素皮膜抵抗 x2

青色LED x12 x1
200Ω炭素皮膜抵抗 x12
ユニバーサル基板 x1

※ケース・レンズフード等は3Dプリンタで自作しました。

仕組み

カメラモジュールを照明機器に向けて使用します。ISO感度自動設定機能を用い、照明を点けた場合、消した場合の設定値を比較することで点灯状態を検知しています。1時間に12回計測し、照明が点いている時間が長ければ時計型LEDをONに、消えている時間の方が短ければLEDをOFFにするという形で記録内容を表現をしました。

特徴

カメラモジュールを用いることで外光に強くなりました。カメラが照明機器を向いていれば、強い西日が入る部屋でも時間帯に関わらず正しく動作することを確認しています。

構成図

構成図

制作工程

Spresenseメインボード・拡張ボード・カメラボード・LCDの動作確認
3Dプリンタを用いたケースの制作
時計型LEDの制作(ハンダ付け)
プログラムの作成
複数環境での動作テスト

工夫・苦労した点

初めはカメラで撮ったライトの画像の輝度から点灯状態を判別しようと思っていたものの、マイコンで画像ファイルを読むのが難しかったのでISO感度を用いることにしました。

また時計型LEDは青色LEDと抵抗器を12個ずつ接続する必要があり、ハンダ付にとても苦労しました。
時計型LEDのハンダ付の様子

カメラを照明に向けやすいように、カメラマウンタは2自由度で向きを調整できるようになっています。
ケース・カメラマウンタのモデル

感想

今回初めてSpresenseを使ってみました!ArduinoやRaspberry Piは比較的使ったことがあったのでとても使いやすかったです。折角なので次回は強みのエッジAI実装に挑戦してみたいです!!

ソースコード

//アドレス0x27 16文字2行の液晶
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

//カメラモジュール
#include <SDHCI.h>
#include <stdio.h>  /* for sprintf */
#include <Camera.h>
#define BAUDRATE                (115200)

SDClass  theSD;
int iso_on = 0;




/**
 * Print error message
 */

void printError(enum CamErr err)
{
  Serial.print("Error: ");
  switch (err)
    {
      case CAM_ERR_NO_DEVICE:
        Serial.println("No Device");
        break;
      case CAM_ERR_ILLEGAL_DEVERR:
        Serial.println("Illegal device error");
        break;
      case CAM_ERR_ALREADY_INITIALIZED:
        Serial.println("Already initialized");
        break;
      case CAM_ERR_NOT_INITIALIZED:
        Serial.println("Not initialized");
        break;
      case CAM_ERR_NOT_STILL_INITIALIZED:
        Serial.println("Still picture not initialized");
        break;
      case CAM_ERR_CANT_CREATE_THREAD:
        Serial.println("Failed to create thread");
        break;
      case CAM_ERR_INVALID_PARAM:
        Serial.println("Invalid parameter");
        break;
      case CAM_ERR_NO_MEMORY:
        Serial.println("No memory");
        break;
      case CAM_ERR_USR_INUSED:
        Serial.println("Buffer already in use");
        break;
      case CAM_ERR_NOT_PERMITTED:
        Serial.println("Operation not permitted");
        break;
      default:
        break;
    }
}

/**
 * Callback from Camera library when video frame is captured.
 */

void CamCB(CamImage img)
{

  /* Check the img instance is available or not. */

  if (img.isAvailable())
    {

      /* If you want RGB565 data, convert image data format to RGB565 */

      img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565);

      /* You can use image data directly by using getImgSize() and getImgBuff().
       * for displaying image to a display, etc. */
      /*
      Serial.print("Image data size = ");
      Serial.print(img.getImgSize(), DEC);
      Serial.print(" , ");

      Serial.print("buff addr = ");
      Serial.print((unsigned long)img.getImgBuff(), HEX);
      Serial.println("");
      */
    }
  else
    {
      Serial.println("Failed to get video stream image");
    }
}


//時計型LEDへの出力
void clock_write(bool *state){
  for (int i=0;i<12;i++){
    if (state[i]==1){
      digitalWrite(i, HIGH);  
    } else{
      digitalWrite(i, LOW);  
    }
  }
}

void led_control(bool val){
  if (val==true){
    digitalWrite(LED0, HIGH);
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
  } else{
    digitalWrite(LED0, LOW);
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    digitalWrite(LED3, LOW);
  }
}

bool check_light(){
  /*ISO感度を取得*/
  led_control(true);
  int iso = theCamera.getISOSensitivity(); //ISO感度取得
  char sendPacket_left[24] = "";  // 値を右詰3桁に変換
  sprintf(sendPacket_left, "%03d", iso);
  
  /*LCDに表示*/
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(sendPacket_left);
  lcd.setCursor(0, 1);
  bool result = (iso <= iso_on);
  if (result){
    lcd.print("Light ON");
  } else{
    lcd.print("Light OFF");
  }
  led_control(false);
  return result;
}




int time_set = 0; //時間の設定(0~12)
bool state[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //時計の状態を保存

void setup() {
  Serial.begin(BAUDRATE);
  /*LCDのセットアップ*/
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Setup...");
  
  //LED
  pinMode(LED0, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);

  //時計型LEDのセットアップ
  for (int i=0;i<12;i++){
    pinMode(i, OUTPUT);
  }
  clock_write(state); //全てオフに

  //INPUT IOの設定
  int change = 13;
  int select = 12;
  pinMode(change, INPUT);
  pinMode(select, INPUT);

  //時間設定
  lcd.clear();
  lcd.print("Time setting");
  digitalWrite(time_set, HIGH);
  Serial.println("時間設定スタート");
  while(digitalRead(select)){ //決定ボタンが押されたら次へ進む(LOW)
    if (!digitalRead(change)){
      digitalWrite(time_set, LOW);  //チェンジボタンが押されたら、次のLEDを点灯させる
      time_set = (time_set+1)%12;
      Serial.print("設定中: ");
      Serial.println(time_set); 
      digitalWrite(time_set, HIGH);
      delay(400);
    }
  }
  Serial.print("設定時間: ");
  Serial.println(time_set);
  
  /*カメラのセットアップ*/
  {
  CamErr err;

  /* Open serial communications and wait for port to open */

  while (!Serial)
    {
      ; /* wait for serial port to connect. Needed for native USB port only */
    }

  /* Initialize SD */
  while (!theSD.begin()) 
    {
      /* wait until SD card is mounted. */
      Serial.println("Insert SD card.");
    }

  /* begin() without parameters means that
   * number of buffers = 1, 30FPS, QVGA, YUV 4:2:2 format */

  Serial.println("Prepare camera");
  err = theCamera.begin();
  if (err != CAM_ERR_SUCCESS)
    {
      printError(err);
    }

  /* Start video stream.
   * If received video stream data from camera device,
   *  camera library call CamCB.
   */

  Serial.println("Start streaming");
  err = theCamera.startStreaming(true, CamCB);
  if (err != CAM_ERR_SUCCESS)
    {
      printError(err);
    }

  /* Auto white balance configuration */

  Serial.println("Set Auto white balance parameter");
  err = theCamera.setAutoWhiteBalanceMode(CAM_WHITE_BALANCE_DAYLIGHT);
  if (err != CAM_ERR_SUCCESS)
    {
      printError(err);
    }
 
  /* Set parameters about still picture.
   * In the following case, QUADVGA and JPEG.
   */

  Serial.println("Set still picture format");
  err = theCamera.setStillPictureImageFormat(
     //CAM_IMGSIZE_QUADVGA_H,
     //CAM_IMGSIZE_QUADVGA_V,
     CAM_IMGSIZE_QVGA_H, 
     CAM_IMGSIZE_QVGA_V,
     CAM_IMAGE_PIX_FMT_JPG);
  if (err != CAM_ERR_SUCCESS)
    {
      printError(err);
    }


  /*照明の設定*/
  lcd.clear();
  delay(10);
  lcd.print("Turn OFF light");
  lcd.setCursor(0, 1);
  lcd.print("and push Select");
  while(digitalRead(select)){} //ボタンが押されるまで待機
  int initial_iso =  theCamera.getISOSensitivity();
  Serial.print("Initial ISO: ");
  Serial.println(initial_iso);
  
  lcd.clear();
  lcd.print("turn ON light");
  while(theCamera.getISOSensitivity() >= initial_iso){
    lcd.clear();
    lcd.print("turn ON light");
    lcd.setCursor(0, 1);
    lcd.print(theCamera.getISOSensitivity());
    delay(500);
    }
  delay(2000);
  iso_on = theCamera.getISOSensitivity();

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Ready");
  lcd.setCursor(0, 1);
  lcd.print("ISO=");
  lcd.setCursor(5, 1);
  lcd.print(iso_on,DEC);
  Serial.print("ISO_on: ");
  Serial.println(iso_on, DEC);
    
  delay(500);
  }
}

void loop() {
  
  int hour_data = 0; //1時間で何回ついていたかを取得(1時間で12回計測)

  Serial.print("check: ");
  for (int i=0;i<12;i++){
    bool result = check_light();
    Serial.print(result);
    hour_data = hour_data + result;
    
    unsigned long start_time = millis();
    while (millis()-start_time<6000) { //300000
        check_light();
        digitalWrite(time_set, HIGH);
        delay(700);
        check_light();
        digitalWrite(time_set, LOW);
        delay(700);
      }
  }
  Serial.println("");

  //1時間分のデータを計算
  bool hour_result = (float(hour_data)/12.0 >= 0.5);
  if (hour_result){
    state[time_set] = true;
  }else{
    state[time_set] = false;
  }
  clock_write(state);
  Serial.print("Hour check: ");
  Serial.println(hour_result);
  time_set = (time_set+1)%12;

  //theCamera.end();
}
ログインしてコメントを投稿する