アダルトグッズメーカーSSI JAPAN スタッフブログ

〈SSIジャパン〉スタッフによる、ゆる~いブログです。 おすすめ商品の紹介はもちろん、とっておきの開発秘話から最新イベントレポート、はてはキワモノ実験や、スタッフの日常風景まで、隔週金+αで更新中。 どうぞ気軽にお立ち寄りください。 公式サイトはhttp://www.ssi-japan.net/

わたくし、bb-conの解説担当のビビ子ですわ!!
今回もよろしくお願いしますわねッ!!

今回は「改造bb-con」の第16回!!最終回ですわ!!
ついにおっぱいグッズの先端タッチセンサを設置しますわよ!!
よくここまでついてきたわね!
誉めて差し上げますわ!!

前回はbb-conをbb-con化するコードを公開しましたわ!
今回は最終目的のおっぱいグッズの先端にタッチセンサを設置しますわ!!

今回は導電性の糸を使いますわ!!
導電糸はアキバの千石電商でも売ってますし
100円ショップでもスマホ向け手袋用に売っていたりしますわ!
用途から考えると色は黒くない方が好ましいですわね!!

この糸を裁縫用の針に通して…

おっぱいグッズの…

乳首から…

ブスリ!!

ギャー!!!
痛くない痛くない痛くない

そのまま針を貫通させて、糸を通しますわ!
乳首の先から若干糸が見える程度に糸は残して、
おっぱいグッズの裏から延びる糸はbb-conのタッチセンサの一つに接続します

乳首タッチセンサ実装概要
乳首タッチセンサ実装概要図

「接続する」と簡単に言いましたが、
糸と銅パッドを直接接続するのは割と難しいですわよね。
そこで金属クリップを銅パッドに半田で取り付けて、
そこにクリップに糸を結ぶ
くらいが現実的な加工になるかと思いますわね。
ここまできますと

乳首にタッチする
タッチセンサが反応する
ジョイスティックのボタン送信
(又はマウスボタン送信)

と動作するようにプログラムを書き換えればいい事がわかりますわね!

今回はあえてコードは公開しませんわ!
どのPADに接続するかとか、
どんな反応をさせるかまで書いても意味ありませんから!
でもこれまでの解説と前回のコードを組み合わせれば
割と簡単に実装できると思いますわよ!!

それに「自分はこんな実装したよ!」と公開する楽しみも
あるかと思いますの。

では今回はここまでにしますわね!!
またお会いできる機会はあると思いますわ!!
それまでごきげんよう!!

わたくし、bb-conの解説担当のビビ子ですわ!!
今回もよろしくお願いしますわねッ!!

今回は「改造bb-con」の第15回!!
最終的におっぱいグッズの先端タッチセンサを設置する所まで解説するつもりよ!!
ちゃんとついてきなさい!

前回はbb-conをスマートポインタのライブラリの追加と
クラスファイルの追加方法を解説しましたわ!!
今回はいよいよbb-conをbb-con化するコードです!!

前回の説明でスマートポインタのライブラリは問題なくインストールされた
ものとして進めますわね!

まずcCapacitiveSensクラスですわ!
これは圧力センサの圧力のかかり具合を-1~+1の範囲で管理するクラスですのよ。
Calibration();で中央値を決めます。
ノイズ除去として指定した回数分(最大8回)の過去の測定値の平均値を使います。
過去の測定値の最大値を1、最小値を-1とします。
コンストラクタで暫定的に最大値・最小値を指定しますが、
それを超える測定値が出た時は更新します。

cCapacitiveSens.h
#pragma once
#include "cAnalogSensGauge.h"
#include "CapacitiveSensor.h"
#include <ArxSmartPtr.h>

//静電容量センサを管理するクラス

class cCapacitiveSens {

private:

    //メンバ
    std::shared_ptr<CapacitiveSensor> mCapacitiveSensor;  //静電容量センサ
    std::shared_ptr<cAnalogSensGauge> mAnalogSensGauge;   //アナログセンサゲージ

    //サンプリングに関する変数
    uint32_t  mSampleArray[8];      //過去のサンプル配列(ノイズ除去用)
    uint8_t   mSampleArrayIndex;    //サンプル配列のインデックス
    uint8_t   mSampleCount;         //1度のセンシングのサンプル回数
    uint8_t   mSmoothSampleCount;   //平滑化に用いるサンプル数
    uint8_t   mDefaultSampleCount;  //起動時に中央値を得る為のサンプリング回数
    uint32_t  mDefaultMinWidth;     //最小値までの幅
    uint32_t  mDefaultMaxWidth;     //最大値までの幅

    //現在の値に関する変数
    double    mNowDoubleValue;      //現在の値(ノイズ除去済)浮動小数
    
    //その他、汎用変数
    uint32_t  mBufIndex;            //バッファループ等に使用

  public:
  
    //初期化
    cCapacitiveSens(uint8_t   SendPin,            //送信ピン
                    uint8_t   ReceivePin,         //受信ピン                
                    uint8_t   SampleCount,        //1度のセンシングのサンプル回数
                    uint8_t   DefaultSampleCount, //起動時に中央値を得る為のサンプリング回数
                    uint8_t   SmoothSampleCount,  //平滑化に用いるサンプル数
                    uint32_t  DefaultMinWidth,    //最小値までの幅
                    uint32_t  DefaultMaxWidth     //最大値までの幅
                    );
    double Update();      //更新してmNowDoubleValueを返す
    double GetNowValue(); //現在のmNowDoubleValueを返す
    void   Calibration(); //校正する
};

cCapacitiveSens.cpp

#include "cCapacitiveSens.h"

//コンストラクタ
cCapacitiveSens::cCapacitiveSens(
      uint8_t   SendPin,            //送信ピン
      uint8_t   ReceivePin,         //受信ピン
      uint8_t   SampleCount,        //1度のセンシングのサンプル回数
      uint8_t   DefaultSampleCount, //起動時に中央値を得る為のサンプリング回数
      uint8_t   SmoothSampleCount,  //平滑化に用いるサンプル数
      uint32_t  DefaultMinWidth,    //最小値までの幅
      uint32_t  DefaultMaxWidth     //最大値までの幅
  ){
  //引数のチェック

  //1度のセンシングのサンプル回数
  if(SampleCount<1){SampleCount=1;} //最小値をチェック
  mSampleCount = SampleCount;       //メンバに登録

  //起動時に中央値を得る為のサンプリング回数
  if (DefaultSampleCount < 1) {DefaultSampleCount=1;} //最小値をチェック
  mDefaultSampleCount = DefaultSampleCount;           //メンバに登録

  //平滑化に用いるサンプル数
  if (SmoothSampleCount < 1) { SmoothSampleCount = 1;}  //最小値をチェック
  mSmoothSampleCount = SmoothSampleCount;               //メンバに登録

  //最小値までの幅
  if (DefaultMinWidth < 1) {DefaultMinWidth = 1;} //最小値をチェック
  mDefaultMinWidth = DefaultMinWidth;             //メンバに登録

  //最大値までの幅
  if (DefaultMaxWidth < 1) {DefaultMaxWidth = 1;} //最小値をチェック
  mDefaultMaxWidth = DefaultMaxWidth;             //メンバに登録

  //静電容量センサのインスタンスを作成
  mCapacitiveSensor = std::make_shared<CapacitiveSensor>(SendPin, ReceivePin);
  while(!mCapacitiveSensor){} //インスタンスができるまで待機
  mCapacitiveSensor->set_CS_AutocaL_Millis(0xFFFFFFFF); //自動校正無効
  
}

//校正する
void cCapacitiveSens::Calibration(){
  //まず中央値を定める
  double tmpValue=0;  //仮の合計値
  for (mBufIndex = 0; mBufIndex < mDefaultSampleCount; mBufIndex++) { //サンプリングを繰り返す
    tmpValue += mCapacitiveSensor->capacitiveSensorRaw(mSampleCount); //全てのサンプル値を合計する
  }

  //平均値をmNowIntValueとする
  tmpValue = tmpValue / mDefaultSampleCount;  //現在の値(ノイズ除去済)Raw値

  //アナログセンサゲージを初期化
  mAnalogSensGauge = std::make_shared<cAnalogSensGauge>(tmpValue,       //中央値
                                                        mDefaultMinWidth, //最小値までの幅
                                                        mDefaultMaxWidth  //最大値までの幅
                                                        );
  //バッファに中央値をコピーする
  for (mBufIndex = 0; mBufIndex < mSmoothSampleCount; mBufIndex++) {
    mSampleArray[mBufIndex] = tmpValue;
  }
  mSampleArrayIndex=0;  //サンプル配列のインデックス
  
}

//更新する
double cCapacitiveSens::Update(){
  //静電容量を測定し、バッファに登録する
  mSampleArrayIndex++;  //バッファのインデックスを+1
  if((mSmoothSampleCount -1) < mSampleArrayIndex ){ mSampleArrayIndex = 0; } //配列の範囲を超えたら先頭へ戻す
  
  //静電容量を測定しバッファに格納
  mSampleArray[mSampleArrayIndex]=mCapacitiveSensor->capacitiveSensorRaw(mSampleCount);
  
  //バッファの値を全て合算し、平均を得る
  mNowDoubleValue =0; //現在の値をリセット
  for(mBufIndex =0; mBufIndex < mSmoothSampleCount; mBufIndex++){ mNowDoubleValue += mSampleArray[mBufIndex];}//合算
  //平均値を現在の値としてゲージを更新
  mNowDoubleValue = mAnalogSensGauge->Update(mNowDoubleValue / mSmoothSampleCount);
  
  return mNowDoubleValue;//返す
}

//現在のmNowDoubleValueを返す
double cCapacitiveSens::GetNowValue(){
  return mNowDoubleValue;
}

cAnalogSensGaugeクラスですわ!
これはcCapacitiveSensクラスでの値の範囲を管理するクラスですわ!

cAnalogSensGauge.h
#pragma once

//アナログセンサの最大・最小値を管理するクラス
//中央値と仮の最大値・最小値を起動時に設定し、
//既存の最大値・最小値を超える値が来たら随時更新し、
//入力された値を-1~1の間で返す。

class cAnalogSensGauge{
private:
  //メンバ
   
  //現在の値に関する変数
  double  mNowRawValue; //現在の値(ノイズ除去済)Raw値
  double  mNowValue;    //現在の値(ノイズ除去済)-1~1

  //閾値に関する変数    
  double  mMaxValue;        //最大値
  double  mBaseValue;       //中央値
  double  mMinValue;        //最小値

public:

  //コンストラクタ
  cAnalogSensGauge( double BaseValue,       //中央値
                    double DefaultMinWidth, //最小値までの幅
                    double DefaultMaxWidth  //最大値までの幅
                    );

  //更新してmNowDoubleValueを返す
  double Update(double Value);  //新しい値

  //現在のmNowDoubleValueを返す
  double GetNowValue();         
};

cAnalogSensGauge.cpp

#include "cAnalogSensGauge.h"

//コンストラクタ
//初期化
cAnalogSensGauge::cAnalogSensGauge( double BaseValue,       //中央値
                                    double DefaultMinWidth, //最小値までの幅
                                    double DefaultMaxWidth  //最大値までの幅
                                    ){
  mBaseValue = BaseValue;

  //引数のチェック

  //最小値までの幅
  if (DefaultMinWidth < 0) { DefaultMinWidth = 0; } //最小値をチェック
  mMinValue= mBaseValue - DefaultMinWidth;           //最低値を設定

  //最大値までの幅
  if (DefaultMaxWidth < 0) { DefaultMaxWidth = 0; } //最大値をチェック
  mMaxValue = mBaseValue + DefaultMaxWidth;          //最大値を設定

  //現在の値を更新する
  mNowRawValue=mBaseValue;  //RAW値
  mNowValue   =0;           //返答用の値

  return;
}

//更新する
double cAnalogSensGauge::Update(double Value) {
  //静電容量を測定し、バッファに登録する
  mNowRawValue=Value;

  //メンバを更新
  if (mMaxValue < mNowRawValue) { mMaxValue = mNowRawValue; } //最大値を更新
  if (mNowRawValue < mMinValue) { mMinValue = mNowRawValue; } //最小値を更新

  //戻り値を算出する
  if (mBaseValue < mNowRawValue) {  //正の値なら
    mNowValue = (mNowRawValue - mBaseValue) / (mMaxValue - mBaseValue);
  }
  else if (mNowRawValue < mBaseValue) {  //負の値なら
    mNowValue = -1 * (mBaseValue - mNowRawValue) / (mBaseValue - mMinValue);
  }
  else {
    mNowValue = 0;
  }

  return mNowValue;
}

//現在のmNowDoubleValueを返す
double cAnalogSensGauge::GetNowValue() {return mNowValue;}

cCapacitiveAxisクラスですわ!
cCapacitiveSensオブジェクトを二つ使って軸を作っていますの!
つまり
左と右の圧力センサでX軸、
上と下の圧力センサでY軸、
その「軸」を管理するクラスですわね!


cCapacitiveAxis.h
#pragma once
#include "cCapacitiveSens.h"
#include <ArxSmartPtr.h>

//静電容量センサを用いた軸を管理するクラス

class cCapacitiveAxis {

private:

  //メンバ
  std::shared_ptr<cCapacitiveSens> mSensorPositive;  //+方向静電容量センサ
  std::shared_ptr<cCapacitiveSens> mSensorNegative;  //-方向静電容量センサ

  //現在の値に関する変数
  double            mPositiveValue;   //+方向静電容量センサの値
  double            mNegativeValue;   //-方向静電容量センサの値
  double            mNowValue;        //現在の値

  //閾値に関する変数
  double            mSlewThreshold;   //0付近でスルーする閾値

public:
  //コンストラクタ
  cCapacitiveAxis(  uint8_t  PositiveSendPin,     //+方向静電容量センサ送信ピン
                    uint8_t  PositiveReceivePin,  //+方向静電容量センサ受信ピン
                    uint8_t  NegativeSendPin,     //-方向静電容量センサ送信ピン
                    uint8_t  NegativeReceivePin,  //-方向静電容量センサ受信ピン
                    uint8_t  SampleCount,         //1度のセンシングのサンプル回数
                    uint8_t  DefaultSampleCount,  //起動時に中央値を得る為のサンプリング回数
                    uint8_t  SmoothSampleCount,   //平滑化に用いるサンプル数
                    uint32_t DefaultMinWidth,     //最小値までの幅
                    uint32_t DefaultMaxWidth,     //最大値までの幅
                    double   SlewThreshold        //0付近でスルーする閾値
                    );

  double Update();            //更新してmNowValueを返す
  void   Calibration();       //校正する
  double GetNowValue();       //現在のmNowValueを返す
  double GetPositiveValue();  //+方向静電容量センサの値を返す
  double GetNegativeValue();  //-方向静電容量センサの値を返す

};

cCapacitiveAxis.cpp

#include "cCapacitiveAxis.h"

//コンストラクタ
cCapacitiveAxis::cCapacitiveAxis( uint8_t  PositiveSendPin,    //+方向静電容量センサ送信ピン
                                  uint8_t  PositiveReceivePin, //+方向静電容量センサ受信ピン
                                  uint8_t  NegativeSendPin,    //-方向静電容量センサ送信ピン
                                  uint8_t  NegativeReceivePin, //-方向静電容量センサ受信ピン
                                  uint8_t  SampleCount,        //1度のセンシングのサンプル回数
                                  uint8_t  DefaultSampleCount, //起動時に中央値を得る為のサンプリング回数
                                  uint8_t  SmoothSampleCount,  //平滑化に用いるサンプル数
                                  uint32_t DefaultMinWidth,    //最小値までの幅
                                  uint32_t DefaultMaxWidth,    //最大値までの幅
                                  double   SlewThreshold       //0付近でスルーする閾値
                                  ){
  //静電容量センサの初期化
   
  //+方向静電容量センサ
  mSensorPositive = std::make_shared<cCapacitiveSens>(PositiveSendPin,    //送信ピン
                                                      PositiveReceivePin, //受信ピン
                                                      SampleCount,        //1度のセンシングのサンプル回数
                                                      DefaultSampleCount, //起動時に中央値を得る為のサンプリング回数
                                                      SmoothSampleCount,  //平滑化に用いるサンプル数
                                                      DefaultMinWidth,    //最小値までの幅
                                                      DefaultMaxWidth     //最大値までの幅
                                                      );
  //-方向静電容量センサ
  mSensorNegative = std::make_shared<cCapacitiveSens>(NegativeSendPin,    //送信ピン
                                                      NegativeReceivePin, //受信ピン
                                                      SampleCount,        //1度のセンシングのサンプル回数
                                                      DefaultSampleCount, //起動時に中央値を得る為のサンプリング回数
                                                      SmoothSampleCount,  //平滑化に用いるサンプル数
                                                      DefaultMinWidth,    //最小値までの幅
                                                      DefaultMaxWidth     //最大値までの幅
                                                      );

  //その他引数チェック
  
  //0付近でスルーする閾値
  if( SlewThreshold < 0 ){ SlewThreshold = 0; } //下限チェック
  if( 1 < SlewThreshold ){ SlewThreshold = 1; } //上限チェック
  mSlewThreshold = SlewThreshold;   //メンバに登録
  
  //現在の値
  mNowValue=0;

  return;
}

//更新する
double cCapacitiveAxis::Update() {
  //静電容量を測定する
  mPositiveValue = mSensorPositive->Update();  //+方向静電容量センサ
  mNegativeValue = mSensorNegative->Update();  //-方向静電容量センサ

  //軸の値を得る
  mNowValue = mPositiveValue - mNegativeValue;
  if(1 < mNowValue   ){ mNowValue =  1; } //上限
  if( mNowValue < -1 ){ mNowValue = -1; } //下限

  //0付近のスルー処理をする
  if(0 < mNowValue){
    if(mNowValue < mSlewThreshold){mNowValue=0;}
  }else{
    if((-1*mSlewThreshold) < mNowValue){mNowValue=0;}
  }
  return mNowValue;
}

//校正する
void cCapacitiveAxis::Calibration() {
  mSensorPositive->Calibration();  //+方向静電容量センサ
  mSensorNegative->Calibration();  //-方向静電容量センサ
}

double cCapacitiveAxis::GetNowValue     () {return mNowValue;      }  //現在のmNowDoubleValueを返す
double cCapacitiveAxis::GetPositiveValue(){ return mPositiveValue; }  //+方向静電容量センサの値を返す
double cCapacitiveAxis::GetNegativeValue(){ return mNegativeValue; }  //-方向静電容量センサの値を返す


cCapacitiveButtonクラスですわ!
これは側面のタッチセンサを管理するクラスですわ!

cCapacitiveButton.h
#pragma once
#include "CapacitiveSensor.h"
#include "cButtonGauge.h"
#include <ArxSmartPtr.h>

//静電容量センサを用いたボタンを管理するクラス

class cCapacitiveButton{
private:

  //メンバ
  std::shared_ptr<CapacitiveSensor> mCapacitiveSensor;  //静電容量センサ
  std::shared_ptr<cButtonGauge>    mButtonGauge;  //ボタンゲージ

  uint32_t mThreshold;  //閾値

public:
  //コンストラクタ
  cCapacitiveButton(  uint8_t   SendPin,             //静電容量センサ送信ピン
                      uint8_t   ReceivePin,          //静電容量センサ受信ピン
                      uint8_t   GaugeWidth,          //ゲージ幅(1~123)
                      uint32_t  Threshold            //閾値(1回のセンシングでの値)
                      );
  bool Update();            //更新して変化の有無を返す
  bool GetNowValue();       //現在のmNowValueを返す

};

cCapacitiveButton.cpp

#include "cCapacitiveButton.h"

//コンストラクタ
cCapacitiveButton::cCapacitiveButton( uint8_t  SendPin,            //静電容量センサ送信ピン
                                      uint8_t  ReceivePin,         //静電容量センサ受信ピン
                                      uint8_t  GaugeWidth,         //ゲージ幅(1~123)
                                      uint32_t Threshold           //閾値(1回のセンシングでの値)
                                      ){
  //静電容量センサのインスタンスを作成
  mCapacitiveSensor = std::make_shared<CapacitiveSensor>(SendPin, ReceivePin);

  //ボタンゲージの初期化
  mButtonGauge = std::make_shared<cButtonGauge>(GaugeWidth,   //ゲージ幅(1~123)
                                                0.5           //Threshold     //閾値(0~1)
                                                );

  mThreshold=Threshold; //閾値
  
  return;
}

//更新して変化の有無を返す
bool cCapacitiveButton::Update() {
  if(mThreshold < mCapacitiveSensor->capacitiveSensorRaw(1)){  //センシング1回の時15~2500、閾値は50程度が良い
    return mButtonGauge->Update(1);
  }
  return mButtonGauge->Update(0);
} 

bool cCapacitiveButton::GetNowValue() { return mButtonGauge->GetNowValue();}       //現在のmNowDoubleValueを返す


cButtonGaugeクラスですわ!
これは平たく言えばチャタリング防止のクラスですの!
cCapacitiveButtonクラスで使われていますわ!

cButtonGauge.h
#pragma once
#include <Arduino.h>

//ボタンのチャタリング防止用クラス

class cButtonGauge{
private:

  //メンバ
  //現在の値に関する変数
  bool    mNowValue;    //現在の値
  double  mThreshold;   //閾値

  //ゲージに関する変数
  /*
    mGaugeはセンサの値により±1でmGaugeMax~mGaugeMinの間を常に変化する。
    連続で同じ値が続くとmGaugeMaxかmGaugeMinを超え、
    その時にボタンのON/OFFが切り替わる
  */
  uint8_t mGaugeWidth;  //ゲージ幅(1~126)
  uint8_t mGauge;       //現在のゲージ(mGaugeWidth=mGaugeMax-mGaugeMin)
  uint8_t mGaugeMax;    //ゲージの最大値
  uint8_t mGaugeMin;    //ゲージの最小値

public:

  //コンストラクタ
  cButtonGauge( uint8_t  GaugeWidth,  //ゲージ幅(1~123)
                double   Threshold    //閾値(0~1)
                );

  //更新して変化の有無を返す
  bool Update(double Value);  //新しい値(0~1)

  bool GetNowValue();         //現在のmNowValueを返す
};

cButtonGauge.cpp

#include "cButtonGauge.h"

//コンストラクタ
cButtonGauge::cButtonGauge( uint8_t GaugeWidth, //ゲージ幅(1~123)
                            double  Threshold     //閾値(0~1)
                            ) {
  //その他引数チェック
  //ゲージ幅
  if (GaugeWidth < 2) { GaugeWidth = 2; }//上限
  if (126 < GaugeWidth) { GaugeWidth = 126; }//下限
  mGaugeWidth = GaugeWidth;                //メンバに登録
  //閾値
  if (Threshold < 0) { Threshold = 0; }//上限
  if (1 < Threshold) { Threshold = 1; }//下限
  mThreshold = Threshold;              //メンバに登録

  //ゲージの条件を定める
  mGaugeMin = 127 - (GaugeWidth / 2);  //下限
  mGaugeMax = mGaugeMin + GaugeWidth;    //上限
  mGauge = mGaugeMin;                 //現在のゲージ

  //現在の値
  mNowValue = false;

  return;
}

//更新して変化の有無を返す
bool cButtonGauge::Update(double Value) {

  //ゲージを動かす
  if (mThreshold < Value) {
    mGauge++; //閾値を超えたらインクリメント
  }
  else {
    mGauge--; //デクリメント
  }

  if (mGauge < mGaugeMin) {
    mGauge = mGaugeMin;
    if (mNowValue) {  //直前のスイッチの状態がONならば
      mNowValue = false;  //値を変更
      return true;        //イベント発呼
    }
  }
  if (mGaugeMax < mGauge) {
    mGauge = mGaugeMax;
    if (!mNowValue) {  //直前のスイッチの状態がOFFならば
      mNowValue = true;  //値を変更
      return true;       //イベント発呼
    }
  }

  return false;
}

bool cButtonGauge::GetNowValue() { return mNowValue; }  //現在のmNowDoubleValueを返す


最後に本体となるbb-con_sample.inoですわ!
これまでのクラスで宣言した
X・Y軸と3つのタッチセンサ、
表裏・角度の設定と保存、
そしてLEDの表示、ジョイスティック・マウスの機能

ここで実装していますのよ!!

bb-con_sample.ino
#include "cCapacitiveAxis.h"
#include "cCapacitiveButton.h"
#include "cButtonGauge.h"
#include "cAnalogSensGauge.h"
#include "Adafruit_NeoPixel.h"
#include "EEPROM.h"
#include "Mouse.h"
#include "Joystick.h"

//X軸
cCapacitiveAxis AxisX=cCapacitiveAxis(   8,   //+方向静電容量センサ送信ピン
                                        13,   //+方向静電容量センサ受信ピン
                                         8,   //-方向静電容量センサ送信ピン
                                        12,   //-方向静電容量センサ受信ピン
                                         1,   //1度のセンシングのサンプル回数
                                       100,   //起動時に中央値を得る為のサンプリング回数
                                         8,   //平滑化に用いるサンプル数
                                        10,   //最小値までの幅
                                        40,   //最大値までの幅
                                       0.1    //0付近でスルーする閾値
                                        );
//Y軸
cCapacitiveAxis AxisY=cCapacitiveAxis(   8,   //+方向静電容量センサ送信ピン
                                         4,   //+方向静電容量センサ受信ピン
                                         8,   //-方向静電容量センサ送信ピン
                                         5,   //-方向静電容量センサ受信ピン
                                         1,   //1度のセンシングのサンプル回数
                                       100,   //起動時に中央値を得る為のサンプリング回数
                                         8,   //平滑化に用いるサンプル数
                                        10,   //最小値までの幅
                                        40,   //最大値までの幅
                                       0.1    //0付近でスルーする閾値
                                        );
//左上                                        
cCapacitiveButton ButtonLU=cCapacitiveButton( 8,    //静電容量センサ送信ピン
                                              6,    //静電容量センサ受信ピン
                                              4,    //ゲージ幅(1~123)
                                             50     //閾値(1回のセンシングでの値)
                                             );
//左下                                             
cCapacitiveButton ButtonLB=cCapacitiveButton( 8,    //静電容量センサ送信ピン
                                              9,    //静電容量センサ受信ピン
                                              4,    //ゲージ幅(1~123)
                                             50     //閾値(1回のセンシングでの値)
                                              );
//右下
cCapacitiveButton ButtonRB=cCapacitiveButton( 8,    //静電容量センサ送信ピン
                                             10,    //静電容量センサ受信ピン
                                              4,    //ゲージ幅(1~123)
                                             50     //閾値(1回のセンシングでの値)
                                              );

//マウス関連
//Z軸をマウスボタンとして扱う為のメンバ
cAnalogSensGauge  ZButtonGauge=  cAnalogSensGauge(       //Z軸のゲージ
                      0,    //中央値
                      -0.5, //最小値までの幅
                      0.5   //最大値までの幅
                      );
cButtonGauge      LZMouseButton=  cButtonGauge(    //左ボタン 
                      10, //ゲージ幅(1~123)
                      0.5 //閾値(0~1)
                      );
cButtonGauge      RZMouseButton=  cButtonGauge(    //右ボタン 
                      10, //ゲージ幅(1~123)
                      0.5 //閾値(0~1)
                      );

//LED関連
#define PIN 20         //LEDを制御するシリアルコマンドを送信するピン番号
#define led_numbers 8  //LEDの数
Adafruit_NeoPixel strip = Adafruit_NeoPixel(led_numbers, PIN, NEO_GRB + NEO_KHZ800); //LED制御ライブラリのクラス・インスタンス

//モード
uint8_t ModesIndex =  1;
uint8_t ModesCount = 24;
int     ModesIndexEepAddr = 0; //現在設定されているモードを格納するEEPROMのアドレス

//モード情報の構造体
struct stMode {
  uint8_t Face;     //0:表,1:裏
  uint8_t Rotate;   //回転(0,1,2,3で表現。x90°回転)
  uint8_t Mode;     //255:シリアルのみ,1:マウス,2:マウス(クリックなし),3:ジョイスティック
  uint8_t FrontLED; //表側として表示するLED(各ビットが各LEDに対応している)
  uint8_t RearLED;  //裏型として表示するLED(同上)
};

stMode Modes[] = {  //複数のモード情報の配列
  //表裏の別、回転角度/90°、デバイス、表側表示LED、裏側表示LED
  {0,0,1,0b01010001,0b00001000},  //表,  0°,マウス
  {0,1,1,0b01010100,0b00000010},  //表, 90°,マウス  
  {0,2,1,0b00010101,0b10000000},  //表,180°,マウス  
  {0,3,1,0b01000101,0b00100000},  //表,270°,マウス
  {1,0,1,0b10100010,0b00000100},  //裏,  0°,マウス
  {1,1,1,0b10101000,0b00000001},  //裏, 90°,マウス  
  {1,2,1,0b00101010,0b01000000},  //裏,180°,マウス  
  {1,3,1,0b10001010,0b00010000},  //裏,270°,マウス

  {0,0,2,0b01010001,0b00001000},  //表,  0°,マウス
  {0,1,2,0b01010100,0b00000010},  //表, 90°,マウス  
  {0,2,2,0b00010101,0b10000000},  //表,180°,マウス  
  {0,3,2,0b01000101,0b00100000},  //表,270°,マウス
  {1,0,2,0b10100010,0b00000100},  //裏,  0°,マウス
  {1,1,2,0b10101000,0b00000001},  //裏, 90°,マウス  
  {1,2,2,0b00101010,0b01000000},  //裏,180°,マウス  
  {1,3,2,0b10001010,0b00010000},  //裏,270°,マウス

  {0,0,3,0b01010001,0b00001000},  //表,  0°,ジョイスティック
  {0,1,3,0b01010100,0b00000010},  //表, 90°,ジョイスティック  
  {0,2,3,0b00010101,0b10000000},  //表,180°,ジョイスティック
  {0,3,3,0b01000101,0b00100000},  //表,270°,ジョイスティック
  {1,0,3,0b10100010,0b00000100},  //裏,  0°,ジョイスティック
  {1,1,3,0b10101000,0b00000001},  //裏, 90°,ジョイスティック
  {1,2,3,0b00101010,0b01000000},  //裏,180°,ジョイスティック
  {1,3,3,0b10001010,0b00010000}   //裏,270°,ジョイスティック
};

//表示関連
long          LEDModeOnTime = 5000;  //モードを表示しているLEDを点灯し続ける時間
unsigned long LEDModeOffTime = 0;     //モードを表示しているLEDを消灯する時刻
bool          LEDVisible = false; //LEDが点灯状態かどうか

//タッチセンサ関連
bool          TouchChanging = false;      //どれかのタッチボタンが操作されたかどうかのフラグ
bool          TouchDowning = false;      //どれかのタッチボタンが押下されているかどうかのフラグ

//タッチセンサ関連の表示モード変更
bool          ModeSelecting = false;  //動作モードを選択中かどうかのフラグ
unsigned long ModeSelectingOutTime = -1;     //動作モード選択中をやめて再起動する時刻
long          ModeSelectingInterval = 5000;   //動作モード選択中の最後の操作からModeSelectingOutTimeまでの間隔(ミリ秒)

//ジョイスティックインスタンス作成
Joystick_ Joystick( JOYSTICK_DEFAULT_REPORT_ID, //レポートID
                    JOYSTICK_TYPE_GAMEPAD,      //ジョイスティックのタイプ
                    3,                          //ボタン数
                    0,                          //ハットスイッチの数
                    true, true, true,           //X/Y/Z軸の方向
                    false,false,false,          //X/Y/Z軸の回転
                    false,                      //ラダー
                    false,                      //スロットル
                    false,                      //アクセル
                    false,                      //ブレーキ
                    false                       //操舵
                    );

void setup() {
  //EEPROMから現在設定されているモードを得る
  ModesIndex = EEPROM.read(ModesIndexEepAddr);
  if (ModesIndex < 1) { ModesIndex = 16; }
  if (ModesCount < ModesIndex) { ModesIndex = 16; }

  //LED制御に関する処理
  strip.begin();  //開始
  strip.show();   //表示(消す)
  startLED();     //開始時のデモ表示を始める
  showLEDMode();  //現在のモードを表示する
}

//メインループ内で使う汎用変数
//変数の宣言は地味に重いのでなるべく回数を減らす
double  SendAxisX;     //送信するX軸値
double  SendAxisY;     //送信するY軸値
double  SendAxisZ;     //送信するZ軸値
double  tmpAxis;       //仮軸
bool    ButtonEvent;   //ボタンのイベントフラグ
bool    ButtonDowning; //どれかのボタンが押下中か
uint8_t RotateIndex;   //回転回数

void loop() {
  OffLEDMode();//LEDの消灯確認する

  ButtonEvent=false;  //ボタンイベントフラグをリセット

  //各センサの値を更新する
  SendAxisX=AxisX.Update();    //X軸
  SendAxisY=AxisY.Update();    //Y軸
  ButtonEvent = ButtonEvent || ButtonLU.Update(); //左上ボタン
  ButtonEvent = ButtonEvent || ButtonLB.Update(); //左下ボタン
  ButtonEvent = ButtonEvent || ButtonRB.Update(); //右下ボタン

  //Z軸を得る
  SendAxisZ = AxisX.GetPositiveValue()  //右
            + AxisX.GetNegativeValue()  //左
            + AxisY.GetPositiveValue()  //上
            + AxisY.GetNegativeValue(); //下
  ZButtonGauge.Update(SendAxisZ);

  //表裏・向きに併せる
  //Z軸を90°回転させる
  RotateIndex = 0;  //リセット
  while (RotateIndex != Modes[ModesIndex].Rotate) {
    tmpAxis = SendAxisX;
    SendAxisX = -1 * SendAxisY;
    SendAxisY = tmpAxis;
    RotateIndex++;
  }
  if (1 == Modes[ModesIndex].Face) {
    SendAxisX = -1 * SendAxisX;
  } //裏面の時はX軸を反転

//デバイスとして返答する
  switch (Modes[ModesIndex].Mode) {
  case 1: //マウスの時
    //XYをセット
    Mouse.move(int(SendAxisX * 100), int(SendAxisY * 100), 0);
    //ボタンをセット
    if(0<ZButtonGauge.GetNowValue()){ // Z軸が正なら左ボタンとして扱う
      Mouse.release(MOUSE_RIGHT);       //右ボタンは必ず解放する
      if (LZMouseButton.Update(ZButtonGauge.GetNowValue())) {  //ボタンに変化があった時
        if (LZMouseButton.GetNowValue()) {  //ボタンが押下された時
          Mouse.press(MOUSE_LEFT);
        }
        else {  //ボタンが上がった時
          Mouse.release(MOUSE_LEFT);
        }
      }
    }
    if (ZButtonGauge.GetNowValue()<0) { // Z軸が負なら右ボタンとして扱う
      Mouse.release(MOUSE_LEFT);          //左ボタンは必ず解放する
      if (RZMouseButton.Update(ZButtonGauge.GetNowValue())) {  //ボタンに変化があった時
        if (!RZMouseButton.GetNowValue()) {  //ボタンが押下された時
          Mouse.press(MOUSE_RIGHT);
        }
        else {  //ボタンが上がった時
          Mouse.release(MOUSE_RIGHT);
        }
      }
    }
    break;
  case 2: //マウス(クリック無し)の時
    //XYをセット
    Mouse.move(int(SendAxisX * 100), int(SendAxisY * 100), 0);
    break;
  case 3: //ジョイスティックの時
    Joystick.setXAxis(int(SendAxisX * 512) + 512);
    Joystick.setYAxis(int(SendAxisY * 512) + 512);
    Joystick.setZAxis(int(SendAxisZ * 512) + 512);
    break;
  }

  //タッチセンサ部分-------------------------------------------------------------------------

  //まずタッチセンサが操作されたかどうかを調べる
  TouchDowning = false;
  ButtonDowning = false;  //ボタン押下フラグをリセット
  ButtonDowning = ButtonDowning || ButtonLU.GetNowValue();//左上
  ButtonDowning = ButtonDowning || ButtonLB.GetNowValue();//左下
  ButtonDowning = ButtonDowning || ButtonRB.GetNowValue();//右下

  //動作モードで分岐
  if (LEDVisible) { //LEDが点灯中なら
    if (ModeSelecting) {  //動作モード選択中ならば
      if (ModeSelectingOutTime < millis()) {  //リブート時刻を超えていた時は
        ModeSelecting = false; //動作モード選択から抜ける
        ModeSelectingOutTime = -1;    //リブート時刻をリセット(リブートしない時用)
      }
      //リブート前ならば
      if (ButtonEvent) {  //ボタン操作があった時
        if (!ButtonDowning) { //ボタンUPは
          ModeSelecting = true;                                   //動作モード選択へ移行
          ModesIndex++;                                         //次のモードへ移行
          if (ModesCount <= ModesIndex) { ModesIndex = 0; }             //整形
          EEPROM.write(ModesIndexEepAddr, ModesIndex);           //EEPROMに新しいモードを保存
          showLEDMode();                                        //LEDで新しいモードを表示する
          ModeSelectingOutTime = millis() + ModeSelectingInterval;  //リブート時刻を決める
        }
      }
    }else {  //動作モード選択中ではない時
      if (ButtonEvent) {  //ボタン操作があった時
        if (ButtonDowning) { //押下時は
          ModeSelecting = true; //動作モード選択へ移行
        }
      }
    }
  }else {  //LEDが点灯中でない時
    if (ButtonEvent) {  //ボタン操作があった時
      if (ButtonDowning) { //押下時は
        ModeSelecting = false;
        showLEDMode();      //現在のモードを表示
      }
    }
    if (!ModeSelecting) { //動作選択モードでない時は
    }
  }

  delay(10);  //必ず入れる。LEDのマイコンがリセットされない
}

void OffLEDMode() {
  //LEDを消灯する
  if (LEDVisible) { //点灯中で
    if (LEDModeOffTime < millis()) {  //消灯予定を超えている時は
      //消灯する
      for (long LedNo = 0; LedNo < 8; LedNo++) { strip.setPixelColor(LedNo, 0, 0, 0); }  //LEDの明るさを割り当てる
      strip.show();  //表示
      LEDVisible = false;  //点灯中のフラグを下げる

      //校正をするフラグを立てる
      AxisX.Calibration();    //X軸
      AxisY.Calibration();    //Y軸

      delay(100);
    }
  }
}

void showLEDMode() {
  //現在のモード設定をLEDで表示する

  //一旦全てのLEDを消す
  for (long LedNo = 0; LedNo < 8; LedNo++) { strip.setPixelColor(LedNo, 0, 0, 0); }  //LEDの明るさを割り当てる

  //表示色を決める
  uint32_t TopColor = strip.Color(0, 255, 255); //上を表示する色(上面の上・右・左の色)(仮)
  uint32_t BottomColor = strip.Color(255, 0, 0); //下を表示する色(下面の下        の色)(赤)
  switch (Modes[ModesIndex].Mode) {  //マウス・ジョイスティック
  case 1:TopColor = strip.Color(0, 255, 0); break; //上を表示する色(上面の上・右・左の色)(緑)//マウス
  case 2:TopColor = strip.Color(0, 255, 255); break; //上を表示する色(上面の上・右・左の色)(青)//マウス(クリックなし)
  case 3:TopColor = strip.Color(0, 0, 255); break; //上を表示する色(上面の上・右・左の色)(青)//ジョイスティック
  default:TopColor = strip.Color(255, 255, 255); break; //上を表示する色(上面の上・右・左の色)(白)//シリアル出力のみ
  }

  //LEDの発光データを作成する
  //表側
  unsigned char FrontLED_0 = Modes[ModesIndex].FrontLED; FrontLED_0 = (FrontLED_0 >> 0) % 2; if (0 < FrontLED_0) { strip.setPixelColor(0, TopColor); }
  unsigned char FrontLED_1 = Modes[ModesIndex].FrontLED; FrontLED_1 = (FrontLED_1 >> 1) % 2; if (0 < FrontLED_1) { strip.setPixelColor(1, TopColor); }
  unsigned char FrontLED_2 = Modes[ModesIndex].FrontLED; FrontLED_2 = (FrontLED_2 >> 2) % 2; if (0 < FrontLED_2) { strip.setPixelColor(2, TopColor); }
  unsigned char FrontLED_3 = Modes[ModesIndex].FrontLED; FrontLED_3 = (FrontLED_3 >> 3) % 2; if (0 < FrontLED_3) { strip.setPixelColor(3, TopColor); }
  unsigned char FrontLED_4 = Modes[ModesIndex].FrontLED; FrontLED_4 = (FrontLED_4 >> 4) % 2; if (0 < FrontLED_4) { strip.setPixelColor(4, TopColor); }
  unsigned char FrontLED_5 = Modes[ModesIndex].FrontLED; FrontLED_5 = (FrontLED_5 >> 5) % 2; if (0 < FrontLED_5) { strip.setPixelColor(5, TopColor); }
  unsigned char FrontLED_6 = Modes[ModesIndex].FrontLED; FrontLED_6 = (FrontLED_6 >> 6) % 2; if (0 < FrontLED_6) { strip.setPixelColor(6, TopColor); }
  unsigned char FrontLED_7 = Modes[ModesIndex].FrontLED; FrontLED_7 = (FrontLED_7 >> 7) % 2; if (0 < FrontLED_7) { strip.setPixelColor(7, TopColor); }
  //裏側
  unsigned char RearLED_0 = Modes[ModesIndex].RearLED;  RearLED_0 = (RearLED_0 >> 0) % 2; if (0 < RearLED_0) { strip.setPixelColor(0, BottomColor); }
  unsigned char RearLED_1 = Modes[ModesIndex].RearLED;  RearLED_1 = (RearLED_1 >> 1) % 2; if (0 < RearLED_1) { strip.setPixelColor(1, BottomColor); }
  unsigned char RearLED_2 = Modes[ModesIndex].RearLED;  RearLED_2 = (RearLED_2 >> 2) % 2; if (0 < RearLED_2) { strip.setPixelColor(2, BottomColor); }
  unsigned char RearLED_3 = Modes[ModesIndex].RearLED;  RearLED_3 = (RearLED_3 >> 3) % 2; if (0 < RearLED_3) { strip.setPixelColor(3, BottomColor); }
  unsigned char RearLED_4 = Modes[ModesIndex].RearLED;  RearLED_4 = (RearLED_4 >> 4) % 2; if (0 < RearLED_4) { strip.setPixelColor(4, BottomColor); }
  unsigned char RearLED_5 = Modes[ModesIndex].RearLED;  RearLED_5 = (RearLED_5 >> 5) % 2; if (0 < RearLED_5) { strip.setPixelColor(5, BottomColor); }
  unsigned char RearLED_6 = Modes[ModesIndex].RearLED;  RearLED_6 = (RearLED_6 >> 6) % 2; if (0 < RearLED_6) { strip.setPixelColor(6, BottomColor); }
  unsigned char RearLED_7 = Modes[ModesIndex].RearLED;  RearLED_7 = (RearLED_7 >> 7) % 2; if (0 < RearLED_7) { strip.setPixelColor(7, BottomColor); }

  strip.show();     //表示
  LEDVisible = true;  //点灯中のフラグを立てる
  LEDModeOffTime = millis() + LEDModeOnTime;   //モードを表示しているLEDを消灯する時刻
  delay(10);
}

void startLED() {
  //起動時の電源ONを明示するLEDデモンストレーション

  long LedNo = 0;
  //緑色をくるくる回す
  long i = 0;
  for (i = 0; i < 50; i++) {
    //明るさを決める
    unsigned long time1 = millis(); //現在時刻を得る
    time1 = i % 8;

    for (LedNo = 0; LedNo < 8; LedNo++) { strip.setPixelColor(LedNo, 0, 255 - ((time1 - LedNo) * 32 - 1),0); } //LEDの明るさを割り当てる
    strip.show(); //表示
    delay(40);
  }

  //徐々に明るく
  i = 0;
  do {
    for (LedNo = 0; LedNo < 8; LedNo++) { strip.setPixelColor(LedNo, 0, i, 0); }  //LEDの明るさを割り当てる
    strip.show(); //表示
    delay(40);
    i += 10;  //少し明るくする
  } while (i < 256);

  //徐々に暗く
  i = 255;
  do {
    for (LedNo = 0; LedNo < 8; LedNo++) { strip.setPixelColor(LedNo, 0, i, 0); }  //LEDの明るさを割り当てる
    strip.show();  //表示
    delay(40);
    i -= 10;  //少し暗くする
  } while (0 < i);

  //消灯
  for (LedNo = 0; LedNo < 8; LedNo++) { strip.setPixelColor(LedNo, 0, 0, 0); }  //LEDの明るさを割り当てる
  strip.show();  //表示
  delay(100);
}

//再起動する
void software_reset() {
  asm volatile ("  jmp 0"); //アセンブラのjmp命令でプログラム実行位置を先頭へ移動する方法
}

実際はこのコードを書き換えるのが
手っ取り早い改造になると思いますが、
前述したとおりこのコードは可読性を重視した(つもりな)ので
ちょっとメモリの使い方を間違うとメモリを使い切ってしまうと思いますわ!!
こうなるとCOMポートにも現れなくなるので、
改めてArduinoブートローダを書きこむ所からやりなおして下さいませ!!

では今回はここまでにしますわね!!
ごきげんよう!!



お外はさむーいので、お家に引きこもりがちなデンマちゃんです。

今日は、年末年始お休みでたっぷり時間があるヨ!
…という素敵なあなたにお勧め‼大人のおもちゃ新製品を3つご紹介いたします。



dennma mask-1_page-0001




まずは、①『ピンクデンマ ゼロ シブヤ』は3.5mのローーーーーーーーングコードなので延長コードいらず‼

美容家電のような美しーい『ピンクゴールド』が高ポイントです。


こちらのスペシャルエディション『シブヤ』には、ワイルドワンオリジナルステッカー(POPで官能的なフルーツモチーフ全6種類)がランダムに1枚入ってます。かわいい‼


4571361303421-01
4571361303421-02




こちらの②『遠隔アナルローター』は驚く飛距離50m‼‼リモコン式のアナルローターです。

1人でイタす時も、リモコン式だと操作しやすいです。ローターは完全防水だから丸洗いできるのもGOODなんです。

アナル開発‼気になってる方も多い人気のジャンルです。

前立腺を圧迫するフォルムと振動でぶっ飛ぶ快感の扉を開いてねー。

アナルは出口だけじゃなく、入口でもあります。


 
4571361303445-02
4571361303445-01





最後に③『ニップルドームワイド』あの『5時に夢中!』で紹介され、チクニーを世に知らしめたニップルドームの進化版。

今回はまるで透明なブラ‼おっぱいを美しく魅せる透き通るカップは視覚的に最上級のエロスなんです。




S__161685513

4582137936552-00
4582137936552-01




さてさて、参考になったかしらん
来年も、良いグッズを世に送り出していけるように頑張りますのでよろしくお願いいたします‼

ワイルドワン広報デンマちゃん
ピンクデンマゼロちゃん

わたくし、bb-conの解説担当のビビ子ですわ!!
今回もよろしくお願いしますわねッ!!

今回は「改造bb-con」の第14回!!
最終的におっぱいグッズの先端タッチセンサを設置する所まで解説するつもりよ!!
ちゃんとついてきなさい!

前回はbb-conをキーボード化してみましたわ!!
今回はbb-conをbb-con化したいと思います!!

ってわけわからないですわよね!?
つまりこれまでの説明でArduino化したらbb-conがbb-conじゃなくなったわけですけど、
これを元のbb-conに戻そうという話ですわ!

とりあえず今までの解説の流れから、
プログラムの可読性を優先しますので、
元のbb-conと全く同じプログラムでは無いと断っておきますわ!
ていうか正直言って全く違いましてよ!!

ある意味、元のbb-conより
レスポンス良くなってるかもしれませんわ…!!

プログラムの可読性を手っ取り早く上げるには
構造化プログラミングに限りますわ!!

でも気を付けないといけない事がいくつかあるのですわ!

Arduino言語はC/C++がベースですのでクラスが作れますの。
クラスが作れるから問題無く構造化プログラミングができます。
そう、構造化プログラミングができるのですが、
あくまでグローバル変数として確保しているオブジェクトのみが保持されるのですわ!!

言い方を変えますと、
クラス内で宣言したローカルオブジェクトは勝手に破棄されるのですわ!!

これに気づかないとハマるのですわ…!!
いえ、むしろ少ないメモリのマイコンプログラミングで
そんな事する方が変なのですけど…。

なので、クラス内で使うオブジェクトはグローバルで宣言して
クラスにポインタを渡して使う、というのが現実的、
無難な折衝具合かと思いますわ!!

ですが、どうせなので可読性を優先して
クラスの中でオブジェクトを宣言して使う方法を試してみますわ!

メモリが潤沢なPC向けのプログラムでもオブジェクト内の
ローカルオブジェクトのポインタが破棄される事には変わりないので
スマートポインタというものを使いますわ!
普通のポインタとどこが違うのかという話は
本筋から離れるのでここではしませんわね。悪しからず!
簡単に言えば、データの寿命管理を厳格にしてるポインタと言えるかしら。

スマートポインタはPC向けのC++では「std::shared_ptr」の様に
標準ライブラリで使えるのですが組み込み用途のArduinoでは使えません
そこでこちらで紹介されているArxSmartPtrを使ってみたいと思います。
こちらからダウンロードしたZIPをインストールして下さいまし!
同じ要領でインストールしますのよ!

さて、もう一つ。クラスを記載する方法ですわ!
C/C++でクラスを宣言する方法は特に説明しませんわ!
端的に言えば、Arduinoでもhファイルとcppファイルを作ればいい事には変わりませんの!
ですが、ArduinoIDEでファイルを追加する方法だけちょっと説明しておきますわね!!

ArduinoIDEのタブが並ぶ行の右端に「▼」マークがあると思いますの。

新規タブ追加メニュー位置
新規タブ追加メニュー位置

これをクリックするとコンテキストメニューが現れますの!

新規タブコンテキストメニュー
新規タブ追加コンテキストメニュー

「新規タブ」
をクリックすると、今度はエディタ画面の下側
「新規ファイルの名前」という入力欄が現れますわ!(これ分かりにくいですわ!!


新規ファイル名入力欄
新規ファイル名入力欄

入力欄にファイル名を入力し、「OK」ボタンを押す
その名前のタブができますわよ!
同時に保存フォルダ内にもファイルが作成されますわ!
こうしてhファイルとcppファイルを一つずつ作っていくのですわよ!

少し記事が長くなったのでコード本体は次回にしますわ!!
もっと長くなりますから!!

では今回はここまでにしますわね!!
ごきげんよう!!

わたくし、bb-conの解説担当のビビ子ですわ!!
今回もよろしくお願いしますわねッ!!

今回は「改造bb-con」の第13回!!
最終的におっぱいグッズの先端タッチセンサを設置する所まで解説するつもりよ!!
ちゃんとついてきなさい!

前回はbb-conをジョイスティック化してみましたわ!!
今回はbb-conをキーボード化したいと思います!!

以前のbb-conのマウス化の際にArduino標準のマウスライブラリがある事を説明しましたが
同じ様にキーボードの標準ライブラリもあるんですのよ!

メインメニューファイルスケッチ例USBKeyboardKeyboardReprogramをクリックすると
簡単なキーボードのサンプルプログラムが出てきますわ!
これをベースにちょろっと書きかえれば簡単にbb-conをキーボードにできますのよ!

#include <CapacitiveSensor.h>
#include "Keyboard.h"
 
//CapacitiveSensorオブジェクト・インスタンスを初期化
CapacitiveSensor cs_8_6 = CapacitiveSensor(8,6);  //タッチセンサ左上
CapacitiveSensor cs_8_9 = CapacitiveSensor(8,9);  //タッチセンサ左下
CapacitiveSensor cs_8_10= CapacitiveSensor(8,10); //タッチセンサ右下
CapacitiveSensor cs_8_4 = CapacitiveSensor(8,4);  //圧力センサ上
CapacitiveSensor cs_8_5 = CapacitiveSensor(8,5);  //圧力センサ下
CapacitiveSensor cs_8_13= CapacitiveSensor(8,13); //圧力センサ右
CapacitiveSensor cs_8_12= CapacitiveSensor(8,12); //圧力センサ左

void setup() {
  Keyboard.begin(); //キーボード開始
}

void loop() {
  //タッチセンサ左上
  if (5000<cs_8_6.capacitiveSensorRaw(30)) {
    //静電容量センサが閾値を超えた時は
    Keyboard.write(KEY_RETURN);//改行
    //Keyboard.write()は1つのキーを押下して解放する
    //送信できる文字列はキーボードに関係する文字・つまりASCII文字のみ
  }
  
  //タッチセンサ左下
  if (5000<cs_8_9.capacitiveSensorRaw(30)) {
    //静電容量センサが閾値を超えた時は
    Keyboard.press(KEY_LEFT_SHIFT); //左シフトを押下
    Keyboard.press('n');            //1文字入力
    //Keyboard.press()は1つのキーを押下する。解放はしない。
    //この場合Shift+n→N(大文字)がタイプされる
    delay(100);
    Keyboard.releaseAll();          //全てのキーを解放
  }

  //タッチセンサ右下
  if (5000<cs_8_10.capacitiveSensorRaw(30)) {
    //静電容量センサが閾値を超えた時は
    Keyboard.println("hoge");
    //Keyboard.println();は連続してキーを押下・解放し、最後に改行と復帰コードを送信する。
  }

  //XYZ軸方向
  if (6500<cs_8_4 .capacitiveSensorRaw(30)){  //上
    Keyboard.print("hello ");
    //Keyboard.print();は連続してキーを押下・解放する。
  }
  if (6000<cs_8_5 .capacitiveSensorRaw(30)){  //下
    Keyboard.print("world ");
  }
  if (6000<cs_8_13.capacitiveSensorRaw(30)){  //右
    Keyboard.print("ssi-japan ");
  }
  if (6500<cs_8_12.capacitiveSensorRaw(30)){  //左
    Keyboard.print("bb-con ");
  }

  delay(1000);  //停止時間を長めにした
}

これを実行するとbb-conはキーボードとして認識されます
テキストエディタを起動してbb-conのタッチセンサや圧力センサを操作すると
テキストエディタに文字が入力されます

キー入力例

bb-conをキーボード化して何をするのか
中々センスが問われると思いますが、
手が不自由な方用のフットスイッチ等に使える気がしますわ!

あと悪用するのは厳禁ですわよ!
昔、PCのUSBポートにUSBメモリを差したら
それはキーボードに偽装したUSBメモリで、
ショートカットキーを巧みに使ってWindowsの設定を書き換えて
あっというまにバックドアを作ったり
そのまま怪しいサイトに何か書きこめばIPがわかりますからハッカーが…
と言った悪事が横行したとかなんとか…

実際に上記のサンプルコードを参考にすれば
こういった悪事が簡単に出来る事だと実感できると思いますわ!
わざわざbb-conで悪事するとも思えないのですが、
モラルの次元ではなく犯罪ですからね!駄目ですわよ!!

では今回はここまでにしますわね!!
ごきげんよう!!

わたくし、bb-conの解説担当のビビ子ですわ!!
今回もよろしくお願いしますわねッ!!

今回は「改造bb-con」の第12回!!
最終的におっぱいグッズの先端タッチセンサを設置する所まで解説するつもりよ!!
ちゃんとついてきなさい!

前回はbb-conをマウス化してみましたわ!!
今回はbb-conをジョイスティック化したいと思います!!

前回のマウス化ではArduino標準のライブラリがありましたが、
ジョイスティック、ゲームパッドの標準ライブラリはありませんの。

ですが、世の中には素晴らしい人がいて、
ジョイスティックのライブラリを作って公開してくれている方がいます。

ライブラリはこちらからダウンロードできますわ!
心広い開発者に感謝してダウンロードしますのよ!

同じ要領でインストールしますのよ!

ちなみにArduinoIDEをインストールしたディレクトリにも
librariesディレクトリがあるのでそちらに入れてもいいですわよ!
ただ、こちらだとArduinoIDEのバージョンが上がった時に
ライブラリのコピペが必要になるのが欠点ですわね。
まぁそれはそれで環境の移動がしやすくて便利ではあるのですが。

ではbb-conでジョイスティックを作ってみます!

#include <CapacitiveSensor.h>
#include <Joystick.h>
 
//CapacitiveSensorオブジェクト・インスタンスを初期化
CapacitiveSensor cs_8_6 = CapacitiveSensor(8,6);  //タッチセンサ左上
CapacitiveSensor cs_8_9 = CapacitiveSensor(8,9);  //タッチセンサ左下
CapacitiveSensor cs_8_10= CapacitiveSensor(8,10); //タッチセンサ右下
CapacitiveSensor cs_8_4 = CapacitiveSensor(8,4);  //圧力センサ上
CapacitiveSensor cs_8_5 = CapacitiveSensor(8,5);  //圧力センサ下
CapacitiveSensor cs_8_13= CapacitiveSensor(8,13); //圧力センサ右
CapacitiveSensor cs_8_12= CapacitiveSensor(8,12); //圧力センサ左

//ジョイスティックのインスタンスを作成
Joystick_ Joystick(
  JOYSTICK_DEFAULT_REPORT_ID, //レポートID
  JOYSTICK_TYPE_GAMEPAD,      //ジョイスティックのタイプ
  3,                          //ボタン数
  0,                          //ハットスイッチの数
  true , true , false,        //X/Y/Z軸の方向
  false, false, false,        //X/Y/Z軸の回転
  false,                      //ラダー
  false,                      //スロットル
  false,                      //アクセル
  false,                      //ブレーキ
  false                       //操舵
);

void setup() {
  Joystick.begin(); //ジョイスティック開始
}

void loop() {

  //タッチセンサ左上
  if (5000<cs_8_6.capacitiveSensorRaw(30)) {
    //静電容量センサが閾値を超えた時は
    Joystick.setButton(0, 1); //ボタン1を押下
  }else {
    //静電容量センサが閾値を超えてない時は
    Joystick.setButton(0, 0); //ボタン1を解放
  }
  
  //タッチセンサ左下
  if (5000<cs_8_9.capacitiveSensorRaw(30)) {
    //静電容量センサが閾値を超えた時は
    Joystick.setButton(1, 1); //ボタン2を押下
  }else {
    //静電容量センサが閾値を超えてない時は
    Joystick.setButton(1, 0); //ボタン2を解放
  }

  //タッチセンサ右下
  if (5000<cs_8_10.capacitiveSensorRaw(30)) {
    //静電容量センサが閾値を超えた時は
    Joystick.setButton(2, 1); //ボタン3を押下
  }else {
    //静電容量センサが閾値を超えてない時は
    Joystick.setButton(2, 0); //ボタン3を解放
  }

  //XYZ軸方向
  int AxisX=0;  //X軸
  int AxisY=0;  //Y軸
  if (6500<cs_8_4 .capacitiveSensorRaw(30)){AxisY--;}//上
  if (6000<cs_8_5 .capacitiveSensorRaw(30)){AxisY++;}//下
  if (6000<cs_8_13.capacitiveSensorRaw(30)){AxisX++;}//右
  if (6500<cs_8_12.capacitiveSensorRaw(30)){AxisX--;}//左
  //強さは1024段階(0~1023)で指定する。中央値は512
  Joystick.setXAxis(int(AxisX*512)+512);//X軸
  Joystick.setYAxis(int(AxisY*512)+512);//Y軸

  delay(100);
}
コードについての説明は注釈で十分かと思いますわ!
ボタン数は最大20個程度だった気がするとか
その位しか書く事ありませんわね…

実際に動作させてもPCが画面側には何も変化はありませんのよ。
Windowsのコントロールパネルデバイスとプリンターを開いて
SparkFun Pro Micro」というゲームコントローラ―のアイコンを、

ゲームパッドアイコン例
ゲームコントローラ―のアイコン例

右クリックして現れたコンテキストメニューの「ゲームコントローラ―の設定」をクリック

ゲームコントローラ―コンテキストメニュー
ゲームコントローラのコンテキストメニュー例

プロパティをクリックして

ゲームコントローラ―の設定ダイアログ
ゲームコントローラのダイアログ例

テストタブをクリックでこんな画面が見えると思いますの。

ゲームコントローラ―のプロパティ
ゲームコントローラのプロパティ

設定したとおりX軸・Y軸とボタン3つ
ゲームパッドになっていますわ!!

bb-conの圧力センサや側面のタッチセンサを触ると
その様子がこの画面で確認できますわ!

では今回はここまでにしますわね!!
ごきげんよう!!

わたくし、bb-conの解説担当のビビ子ですわ!!
今回もよろしくお願いしますわねッ!!

今回は「改造bb-con」の第11回!!
最終的におっぱいグッズの先端タッチセンサを設置する所まで解説するつもりよ!!
ちゃんとついてきなさい!

前回はLEDを光らせてみましたわ!!
今回はbb-conをマウス化してみたいと思います!!

実はArduinoにはマウスのサンプルプログラムがありますのよ!
メインメニューファイルスケッチ例USBMouseButtonMouseControlをクリックすると
マウスのサンプルプログラムが出てきますわ!
これをベースにちょろっと書きかえれば簡単にbb-conをマウスにできますのよ!

では以前作ったタッチセンサー圧力センサーのサンプルを組み合わせて
簡単なマウスを作ってみましょう!

今回はあくまでマウスのサンプルなので、
  • 圧力センサー4つを方向ボタンにする
  • タッチセンサ―2つをマウスボタンにする
という形にしたいと思いますわ!

#include <CapacitiveSensor.h>
#include "Mouse.h"
 
//CapacitiveSensorオブジェクト・インスタンスを初期化
CapacitiveSensor cs_8_9 = CapacitiveSensor(8,9);  //タッチセンサ左下
CapacitiveSensor cs_8_10= CapacitiveSensor(8,10); //タッチセンサ右下
CapacitiveSensor cs_8_4 = CapacitiveSensor(8,4);  //圧力センサ上
CapacitiveSensor cs_8_5 = CapacitiveSensor(8,5);  //圧力センサ下
CapacitiveSensor cs_8_13= CapacitiveSensor(8,13); //圧力センサ右
CapacitiveSensor cs_8_12= CapacitiveSensor(8,12); //圧力センサ左

void setup() {
  Mouse.begin();  //マウス処理開始
}

void loop() {
  
  //タッチセンサ左下
  if (5000<cs_8_9.capacitiveSensorRaw(30)) {
    //左ボタン押下中でなければ左ボタン押下を送信
    if (!Mouse.isPressed(MOUSE_LEFT)) {Mouse.press(MOUSE_LEFT);}
  }else {
    //左ボタン押下中ならば左ボタン解放を送信
    if (Mouse.isPressed(MOUSE_LEFT)) {Mouse.release(MOUSE_LEFT);}
  }

  //タッチセンサ右下
  if (5000<cs_8_10.capacitiveSensorRaw(30)) {
    //右ボタン押下中でなければ右ボタン押下を送信
    if (!Mouse.isPressed(MOUSE_RIGHT)) {Mouse.press(MOUSE_RIGHT);}
  }else {
    //右ボタン押下中ならば右ボタン解放を送信
    if (Mouse.isPressed(MOUSE_RIGHT)) {Mouse.release(MOUSE_RIGHT);}
  }
 
  //マウスの移動
  int AxisX=0;  //X軸移動量
  int AxisY=0;  //Y軸移動量
  if (6500<cs_8_4 .capacitiveSensorRaw(30)){AxisY--;}//上
  if (6000<cs_8_5 .capacitiveSensorRaw(30)){AxisY++;}//下
  if (6000<cs_8_13.capacitiveSensorRaw(30)){AxisX++;}//右
  if (6500<cs_8_12.capacitiveSensorRaw(30)){AxisX--;}//左
  Mouse.move(AxisX*100,AxisY*100,0);  //マウス移動を送信
  
  delay(100);
}

コードについて少し説明しますわね。

マウスボタンのクリックですが
Mouse.isPressed(ボタン)でMouseオブジェクトの任意のボタンが押下中かどうかがわかります
Mouse.press(ボタン)でMouseオブジェクトの任意のボタンが押下された事をPCへ送信します。
Mouse.release(ボタン)でMouseオブジェクトの任意のボタンが解放された事をPCへ送信します。

マウスのクリックはpressの後でreleaseを受け取ったPCが勝手に
「今クリックされた!」と判断してるわけですわね。

マウスの移動ですが、当然マウスからPCへ送るのは「移動量」ですわね。
座標は送りませんわ。
このコードを実行してみると飛び飛びにマウスが移動する事がわかると思いますの。
丁度100pix程度スキップしますわね。
これはMouse.moveに移動量=100にして渡しているからですわ。

実際にマウスを実装する場合はここに移動を入れますのよ。
bb-conは静電容量センサなのでその変化量をMouse.moveに渡せば
滑らかに動くマウスになりますわ!
(今回はマウスのサンプルなのでそこまではやりませんわ)

アナログ値の得られないボタンを使う場合は
カーソルキーの様に一定時間の押しっぱなしの時に送信間隔を早くするといった
工夫が考えられますわね。
その際は最後のdelay();の数字を必要に応じて
小さくするといった処理を加える事になりますわ。

そうですわ。今まで説明してませんでしたが
このdelay(i);は「iミリ秒待つ」という意味ですわ。
ミリ秒は0.001秒ですわ!

メインループの最後には必ずこのdelayで一定時間待機する事を忘れないで下さいませ!!
でなければとても恐ろしい事が起きますのよ…

いえ、何度も再起動を繰り返すのですわ…
マイコンには定期的にdelayを入れないと「バグで無限ループに入った」と判断して
勝手にリセットする仕組みがあるんですのよ。
ウオッチドッグタイマと言う機能なのですが、文字通り番犬ですわね。
逆にこの仕組みを利用して任意にマイコンをリセット・再起動する手法もありますのよ。

あともう一つついでに説明しますが
マウスやジョイスティックを作る際に
delay();を極端に小さくしてはいけませんわ!

例えば1ミリ秒なんて指定したら、PCがフリーズしますわ!
実際はフリーズしてないのですが、
あまりにも大量に送られてくるマウスイベントにPCが対応できなくなり、
他の入力をうけつけなくなり、画面更新が止まります
のよ!

PCの画面のリフレッシュレートは標準で60Hz。
なので1/60秒以上の頻度でマウスイベントを発生させても
意味がないんです。これは16.6ミリ秒ですね。

昨今のゲーミングディスプレイは120Hzや240Hzのリフレッシュレートのものがありますが、
人間がそれだけの操作が可能かというとそれは別の話かと思いますし、
現実にPCがフリーズするのですからdelay()をそこまで小さくする意味がありませんわ。

それにbb-conの用途ならそこまでシビアなタイミングは必要ないですものね。

では今回はここまでにしますわね!!
ごきげんよう!!