電熱ソックスの調整器をSeeed XIAO RP2040, Seeeduino XIAOを活用して製作してみました。
| 電熱ソックス調整器 |
電熱ソックス
使用する電熱ソックスは以下で入手可能なものです。
電熱ソックス1組とバッテリー2個を組み合わせて使用するようになっています。このバッテリーは3段階の強度調整及び電源ON/OFFに対応しています。また付属のリモコンからも3段階の強度調整が可能で、リモコンからは電源OFFのみに対応しています。強度については強中弱それぞれ3.5V, 3.0V, 2.5Vが出力されるようです。靴下の内側のポケット形状になっているところでバッテリーを保持するようになっています。
実際に使用してみると以下の点が気になりました。
- バッテリーはそれなりの重さがあるため、靴下のポケットに入れておくとどうしても歩いた時の遠心力で靴下が下がってきてしまう。
- 温度調節は3段階では少し足りなく、より細かく調整を行いたい。
- 最低の2.5Vでも熱すぎると感じる場合がある。
そこで、付属バッテリーでの調整ではなく、一般のUSBバッテリーと一体化可能な調整器を製作してみました。USBバッテリーはズボンのポケットに入れておいて靴下までのケーブルを接続する方式とします。
DC/DCコンバータ (MP1584)
電圧調整にはステップダウン型で小型のボードが入手可能なMP1584を用いることとしました。通常の使用法では半固定抵抗または固定抵抗により、フィードバック電圧が0.8Vになるように調整されるので、この部分をデジタルポテンショメータに置き換えてマイコンから制御するようにします。
| MP1584のリファレンス回路 |
市販のボードもほぼ上記のリファレンス回路の通りに構成されており、出力電圧は
Vout = 0.8 * (R1 + R2) / R2
で求められます。リファレンス回路は約3.3Vが出力される定数となっているようです。
デジタルポテンショメータによる電圧調整 (CAT5171)
マイコンからポテンショメータの抵抗値を設定できるデバイスとしてデジタルポテンショメータがあります。今回はデジタルポテンショメータとして、256ポイント / 50KΩのCAT5171を使用して、以下のような回路を構成するようにしました。
| MP1584 + CAT5171 |
この構成の場合、ポテンショメータが0Ωで5.28V、50KΩで1.55Vとなります。また仮にポテンショメータがオープン(未接続)となってしまった場合0.8Vが出力されます。このようにポテンショメータの最大値、最小値、オープン時にも大きく問題のない範囲で電圧が出力されるように設計しておき、実際に使用する範囲はマイコンにて設定するようにすれば安全に柔軟性を持たせることができます。
Seeeduino XIAO と Seeed XIAO RP2040
電熱ソックス調節器をできるだけ小型にするために、以前使用したことのあるSeeeduino XIAOをを使用することにしました。同じベンダー(Seeed)から同じピンフォームファクタのSeeed XIAO RP2040が入手可能なので、ソケットで着脱可能として両対応とすることにしました。
なお、他のマイコンボードにはないSeeed XIAO RP2040の特徴としては、NeoPixelを搭載している点が挙げられます。今回は出力電圧レベルを示すインジケータとしてNeoPixelを活用することとします。
今回のプロジェクトのソースコードを以下に配置しました。
https://github.com/elehobica/SocksWarmerVoltageControl
ソースコードのビルドはArduinoを使用して行います。後述するスリープ機能について、RP2040向けのArduino環境ではデフォルトでライブラリが組み込まれていないので、Seeed XIAO RP2040の記載に従ってライブラリを更新してください。
回路図と実体配線
| 実体配線写真 |
注意点ですが、今回のように市販のMP1584ボードを使用する場合、出力電流に応じてかなりの発熱があるのですが、基板が非常に小さいため発熱に対する放熱が充分ではないようで、1.5A程度の出力電流でかなりの発熱になります。したがって実体配線写真はあくまで実験用の参考としていただき、実際の運用時には放熱を充分に行うようにしてください。
電圧調整のリニア化
ポテンショメータの抵抗値とMP1584からの電圧出力値は以下に示すように線形比例関係とはなりません。
したがって所定の電圧を得るためのポテンショメータ値を逆算する関数を設けることで、電圧調整範囲をリニアに分割したポイントでの抵抗値を設定するようにします。
void MP1584byWiper::setLinerVoltagePos(uint16_t pos)
{
if (pos >= _numLinearPos - 1) {
pos = _numLinearPos - 1;
}
double vOutTarget = _vRangeMin + (_vRangeMax - _vRangeMin) * pos / (_numLinearPos - 1);
setVoltage(vOutTarget);
}
...(中略)...
void MP1584byWiper::setVoltage(double voltage)
{
double RwiperTarget = Vref * _Rtop / (voltage - Vref) - _Rbtm;
double RwiperMax = _wiper->getMaxResistance();
double RwiperMin = _wiper->getMinResistance();
if (RwiperTarget < RwiperMin) {
RwiperTarget = RwiperMin;
} else if (RwiperTarget > RwiperMax) {
RwiperTarget = RwiperMax;
}
uint32_t numWiperPos = _wiper->getWiperPositions();
uint32_t RwiperPos = (RwiperTarget - RwiperMin) * (numWiperPos - 1) / (RwiperMax - RwiperMin) + 0.5;
_wiper->setWiper(RwiperPos);
}
電圧ポジション数を11 (0 ~ 10)、電圧調整範囲を2.0V ~ 3.5Vとした場合に以下のようなリニアな電圧調整特性を得ることができます。
| 11ポジション設定時の抵抗値と出力電圧の関係 |
NeoPixelによる設定温度表示
Seeed XIAO RP2040の場合はNeoPixelを使用して出力電圧のレベル設定状況を色で知らせるようにしました。一番低い電圧設定の場合、中間、一番高い電圧設定のそれぞれで青→緑→赤となるように中間色も含めて徐々に色が変わるようにします。NeoPixelの出力はR, G, Bそれぞれにおいて、最大255までを指定できます。ただし、青→緑→赤と変化させたときに輝度レベルが一定を保つように以下のように変化させるようにします。
RGB to YCbCr計算式である以下の式からおおよその値をプログラム中のR: 100, G: 50, B: 255の係数として設定するようにしています。
Y = 0.257R + 0.504G + 0.098B + 16
#include <Adafruit_NeoPixel.h>
#define NUM_NEO 1
#define PIN_NEO_PWR 11
#define PIN_NEOPIX 12
Adafruit_NeoPixel neoPixel(NUM_NEO, PIN_NEOPIX, NEO_GRB + NEO_KHZ800);
void setup()
{
neoPixel.begin();
pinMode(PIN_NEO_PWR, OUTPUT);
digitalWrite(PIN_NEO_PWR, HIGH);
neoPixel.clear();
neoPixel.show();
}
void colorWarmness(uint8_t level, uint8_t warmness)
{
uint8_t r, g, b;
if (level > 100) level = 100;
if (warmness > 100) warmness = 100;
uint8_t rMax = 100 * level / 100;
uint8_t gMax = 50 * level / 100;
uint8_t bMax = 255 * level / 100;
if (warmness < 50) { // B -> G
r = 0;
g = gMax * warmness / 50;
b = bMax * (50 - warmness) / 50;
} else { // G -> R
r = rMax * (warmness - 50) / 50;
g = gMax * (100 - warmness) / 50;
b = 0;
}
neoPixel.clear();
neoPixel.setPixelColor(0, neoPixel.Color(r, g, b));
neoPixel.show();
}
このようにして作成した11段階のグラデーション写真を載せておきます。
| B → G → R グラデーション |
なお、実際のプログラムではNeoPixelの常時点灯は消費電力の観点からも好ましくないので、設定を変更した際に全点灯して徐々に輝度が下がって消灯するようにしました。
電源OFF時のスリープ
最後にSeeeduino XIAOの場合とSeeed XIAO RP2040の場合のスリープの違いについて簡単に説明します。今回はWake Up条件としてピン入力を用いた方法について比較します。
Seeeduino XIAOの場合
Seeeduino XIAOの場合はEnergySavingライブラリを用いて、Wake Up条件として使用するピンおよびWake Up時のコールバック関数を指定します。engySave.standby();をコールすることでスリープモードに入ります。またピンの検出条件を指定する引数はありませんが、下記のコードでH→Lの立ち下がりを検出してWake Upするようです。
#include <TimerTC3.h>
#include <EnergySaving.h>
EnergySaving engySave;
#define PIN_CT 6
...
if (powerDown) {
// Avoid pin 1 ~ 3 because EnergySaving does NOT accept those pins
engySave.begin(WAKE_EXT_INTERRUPT, PIN_CT, wakeUpFunc);
powerDown = 0;
TimerTc3.detachInterrupt();
engySave.standby();
}
Seeed XIAO RP2040の場合
Seeed XIAO RP2040の場合はsleep_goto_dormant_until_pin()関数を用いて、Wake Up条件として使用するピンおよびピンの検出条件を指定します。sleep_goto_dormant_until_pin(ピン番号, true, false);で立ち下がり条件となります。この関数が実行された時点でスリープ(dormant状態)に入りますが、Wake Upする際はこの関数のすぐ次から処理が再開される点がSeeeduino XIAOのスリープと異なる点になります。またスリープの前後でクロック状態の保存、復元も自前で行う必要があります。
#include <pico/time.h>
#include <pico/sleep.h>
#include <hardware/pll.h>
#include <hardware/clocks.h>
#include <hardware/structs/clocks.h>
#include <hardware/structs/scb.h>
#define PIN_CT 6
...
if (powerDown) {
// === goto dormant then wake up ===
uint32_t ints = save_and_disable_interrupts(); // (+a)
_preserve_clock_before_sleep(); // (+c)
//--
sleep_run_from_xosc();
sleep_goto_dormant_until_pin(PIN_CT, true, false); // dormant until fall edge detected
//--
_recover_clock_after_sleep(); // (-c)
restore_interrupts(ints); // (-a)
wakeUpFunc();
}

0 件のコメント:
コメントを投稿