有名ブランドの自動芳香剤が実験の対象として選ばれました。
挑戦する
オリジナルのフレッシュナーには、一定の間隔でスプレー機能がありますが、これは必ずしも便利ではありません。 日中は誰も家にいないので、シリンダーは無駄になります。 さらに、開発者は、他のメーカーのシリンダーの使用に対する「保護」を組み込んでいます。これは、非常に単純ですが、それがなければ、生活しやすくなります。 さらに、「手動」スプレーの場合、シリンダーを取り出して通常のフレッシュナーとして使用する必要がありました。
何を得たかった
まず、作業アルゴリズムをよりインテリジェントにする。 つまり、トイレを訪れた後に芳香剤を機能させることです。 次に、ケースを開かずに「手動」操作の機能を追加します。
解決策
アルゴリズム
光センサーに基づいてスプレーの動作を自動化することにしました。 つまり、5秒後にライトがオフになると、噴霧器が作動するはずです。 その後、15分間ライトのオン/オフに応答しないでください。 呼吸できないように部屋を「リフレッシュ」しないようにします。 また、動作モードに関係なく、「手動」スプレー用のボタンを追加します。
「患者」の剖検
新しいものを追加する前に、私はこの「奇跡の集合体」の中にあるものを見ることにしました。
そして最初に出会ったのは、非標準の「三角形」の頭を持つネジでした。
幸いなことに、適切なドライバーがマイクロドライバーのセットで見つかり、プロセスはそこで止まりませんでした。 内部では、すべてが非常にシンプルであることが判明しました。 下降機構は、スプレーボトルを押す舌に接続された一対のギアのギアボックスを介した従来の電気モーターです。 制御ボードは、それ自体を製造するためのテンプレートとしてだけでなく、電源とトリガーモーターを接続するためのコネクタのソースとしても重要ではありません。
電子部品
このデバイスが何であるかが明らかになったら、独自の制御モジュールの設計を開始できます。 頭脳はマイクロコントローラMSP430G2553になります。 LaunchPadキットには若いモデルG2452もあり、使用できますが、デバッグとキャリブレーションに便利なハードウェアUARTがあるため、古いモデルを選択しました。 たとえば、空の円柱インジケータ。 最初は、スプレーの持続回数がわからないので、プログラムは不揮発性メモリ内のレコードをカウンターに提供し、続いてUARTを介して読み取りと送信を行います(このため、コントローラーを制御モジュールから取り外してデバッグボードに配置する必要があります)。 このデータを受け取ってプログラムを最終バージョンに戻した後、コントローラーを新しいモデルに置き換える予定です。
制御モジュールのスキーム。 これが私の最初の完成したデバイスであるため、回路とボードの両方が品質基準からはほど遠いことにすぐに注意したいです。
このスキームは、インターネットのコントローラーおよび記事に関するデータシートの推奨事項に基づいてまとめられました。 DIHALTに特に感謝します。彼の初心者向けエレクトロニクスチュートリアルが私の出発点でした。
コンポーネントは非常に単純です。 データシートの推奨事項に従ったコンデンサC1およびC2のフィルターは、ADCの精度を向上させます。 フォト抵抗LDR1は光センサーであり、抵抗R3の分圧器を介して接続されています。 降下モーターはBC337トランジスタを介して制御されます。 手動リリースボタンS1と2色のLEDも図に追加されています。 2色のLEDを使用しました。 通常の動作を示すインジケータとして1秒間に1回緑色に点滅し、バッテリーが2.5V未満に放電されると赤色に点滅します。 リアルタイムクロック信号のソースは、32768 HzのクォーツZ1で、ボードとコントローラーが付属しています。
ソフトウェア部
I ++のIAR Embedded Workbenchでコントローラーのプログラムを作成しました。 ここに彼女のコードをコメント付きで持ってきます。
Spary.hセキュリティファイルコード
ヘッダーファイルspray.h
#ifndef __SPRAY_H_ #define __SPRAY_H_ #include <sstream> // #define BUTTON BIT4; // #define SPRAY_PORT BIT6;// P1.6 #define LED_GREEN BIT1; // P2.1 #define LED_RED BIT2; // P2.2 #define LIGHT_SENSOR INCH_5; // A5 #define DARK_VALUE 650; // " ." #define LOWBATT_VALUE 520; // . // unsigned int SleepPeriod = 900; // . 15 . (900) unsigned int SpayDelay = 5; // 5. unsigned int DarkValue = 650; // " ." unsigned int LowBattValue = 520; // . unsigned int BattInterval = 5; // 5. // unsigned int ADCValue; // unsigned int SprayCount; // unsigned int TimerCount = 0; // unsigned int BattTimer = 0; // unsigned int RXByte; // UART // bool ADCDone = false; // bool LightOn = false; // ( ) bool SleepMode = false; // ( 15 ) bool IsCounting = false; // , // bool LowBatt = false; // bool BlinkOn = false; // bool Tick = false; // bool IsADCOn = false; // bool IsADCLight = true; // (true - // , false - ) bool ForceSpay = false; // bool UARTReceived = false; // UART // void UARTSend(string str); // UART void LightSensorOn(); // void VCCSensorOn(); // unsigned int ReadCountFromFlash(); // void WriteCountToFlash(unsigned int); // void Spray(); // #endif
プログラムコード
プログラムのテキスト。 main.cpp:
この形式のプログラムには、シリンダーの終了を示すブロックが含まれていません。これは、執筆時点では、スプレーが何回続くかわからないためです。
#include <msp430g2553.h> #include "spray.h" int main( void ) { // WDT WDTCTL = WDTPW + WDTHOLD; // //------------------------- // - P1DIR &= ~BUTTON; // P1REN |= BUTTON; // P1OUT &= ~BUTTON; // - GND P1IES |= BUTTON; // P1IFG &= ~BUTTON; // P1IE |= BUTTON; // P1DIR |= SPRAY_PORT; // P1OUT &= ~SPRAY_PORT; // // P2DIR |= LED_GREEN P2DIR |= LED_RED; // ( ) P2OUT |= LED_GREEN; P2OUT &= ~LED_RED; // . //------------------------- // DCOCTL = CALDCO_1MHZ; // ~ 1 BCSCTL1 = CALBC1_1MHZ; //------------------------- // TACTL = TASSEL_1 + MC_1; // ACLK (), // CCR0 CCR0 = 0x8000; // .. 32768, // CCTL0 = CCIS0 + OUTMOD0 + CCIE; // , // //------------------------- // UART. FLASH P1SEL = BIT1 + BIT2; // P1SEL2 = BIT1 + BIT2; // UART UCA0CTL1 |= UCSSEL_2; // - SMCLK ~ 1MHz UCA0BR0 = 0x68; // 9600 UCA0BR1 = 0; // 1000000 / 9600 = 104 = 0x68 UCA0MCTL = UCBRS0; UCA0CTL1 &= ~UCSWRST; // IE2 = UCA0RXIE; // UART //------------------------- // Flash FCTL2 = FWKEY + FSSEL1 + FN1; // // . 65535 - // SprayCount = ReadCountFromFlash(); if(SprayCount == 65535) { SprayCount = 0; // WriteCountToFlash(SprayCount); // 0 Flash } //------------------------- __delay_cycles(2000); // //------------------------- __bis_SR_register(GIE); // P2OUT &= ~LED_GREEN; // . . UARTSend("Ready\r\n"); // while(true) { if(Tick) // . { Tick = false; // BattTimer++; if(BattTimer >= BattInterval) // { BattTimer = 0; // VCCSensorOn(); // } if(!SleepMode) // { LightSensorOn(); // if(IsCounting) // { if(TimerCount >= SpayDelay) // { Spray(); // } } } else { // if(TimerCount >= SleepPeriod) { TimerCount = 0; // SleepMode = false; // } } } if(ADCDone) // { ADCDone = false; // if(IsADCLight) // { if(ADCValue < DarkValue) // . { LightOn = true; // " " IsCounting = false; // ( , ) TimerCount = 0; // } else // { if(LightOn) // { LightOn = false; // IsCounting = true; // TimerCount = 0; } } } else // { if(ADCValue < LowBattValue) { LowBatt = true; // } else { LowBatt = false; // - } } } if(ForceSpay) // . . { ForceSpay = false; Spray(); } if(UARTReceived) // UART. . { UARTReceived = false; ostringstream s; s << SprayCount << "\r\n"; UARTSend(s.str()); } } } // UART void UARTSend(string str) { int len = str.length(); for(int i=0;i<len;i++) { while(!(IFG2&UCA0TXIFG)); UCA0TXBUF = str[i]; } } // void LightSensorOn() { if(!IsADCOn) { IsADCOn = true; IsADCLight = true; ADC10CTL0 &= ~ENC; ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE; ADC10CTL1 = ADC10SSEL_2 + LIGHT_SENSOR; ADC10CTL0 |= ENC + ADC10SC; } } // void VCCSensorOn() { if(!IsADCOn) { IsADCOn = true; IsADCLight = false; ADC10CTL0 &= ~ENC; ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + REF2_5V + ADC10IE; ADC10CTL1 = ADC10SSEL_2 + INCH_11; __delay_cycles(128); ADC10CTL0 |= ENC + ADC10SC; } } // unsigned int ReadCountFromFlash() { __bic_SR_register(GIE); // int *Flash_ptr; Flash_ptr = (int *) 0x1040; // *Flash_ptr = 0; // unsigned int value = *Flash_ptr; // __bis_SR_register(GIE); // return value; // } // void WriteCountToFlash(unsigned int value) { __bic_SR_register(GIE); // int *Flash_ptr; Flash_ptr = (int *) 0x1040; // FCTL1 = FWKEY + ERASE; // FCTL3 = FWKEY; *Flash_ptr = 0; // FCTL1 = FWKEY + WRT; *Flash_ptr++ = value; // Flash FCTL1 = FWKEY; FCTL3 = FWKEY + LOCK; // __bis_SR_register(GIE); // } // void Spray() { __bic_SR_register(GIE); // P1OUT |= SPRAY_PORT; // __delay_cycles(500000); // ~ 0.5 ( 1) P1OUT &= ~SPRAY_PORT; // // LightOn=false; SleepMode=true; IsCounting=false; TimerCount=0; SprayCount++; WriteCountToFlash(SprayCount); __bis_SR_register(GIE); // } //--------------------------- // // #pragma vector=ADC10_VECTOR __interrupt void ADC_Handler() { ADCValue = ADC10MEM; // ADCDone = true; // IsADCOn = false; } // #pragma vector=PORT1_VECTOR __interrupt void Button_Handler() { P1IE &= ~BUTTON; // P1IFG &= ~BUTTON; // ForceSpay = true; // // __delay_cycles(300000); P1IE |= BUTTON; // } // 1 . #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_Handler() { // if(!BlinkOn) // { // // - , if(LowBatt) { P2OUT |= LED_RED; } else { P2OUT |= LED_GREEN; } BlinkOn = true; } else // { P2OUT &= ~LED_RED; P2OUT &= ~LED_GREEN; BlinkOn = false; } TimerCount++; // Tick=true; // } // UART #pragma vector=USCIAB0RX_VECTOR __interrupt void UART_Handler() { RXByte = UCA0RXBUF; UARTReceived = true; }
この形式のプログラムには、シリンダーの終了を示すブロックが含まれていません。これは、執筆時点では、スプレーが何回続くかわからないためです。
組立
ボード上のプログラムをコンパイルおよびデバッグした後、ブレッドボード上のテスト用にモジュールがコンパイルされました。
ここでのLaunchPadデバッグボードは、クォーツのホルダーにすぎません。これは、レイアウト上に配置するのが不可能なほど小さいためです。
すべてが失敗することなくアルゴリズムに従って動作すると確信したとき、制御モジュール用のボードの製造を開始しました。これは、ネイティブのものの代わりにハウジングに配置できます。
制御モジュールのボードレイアウト(Sprint-Layoutで作成):
その後、ボードが作成されました。 図面はLUT法で適用され(再びDIHALTに感謝)、ボードは塩化第二鉄でエッチングされ、錫メッキされます。
次に、コンポーネントをはんだ付けします。 マイクロコントローラはソケットに配置されているため、プログラムの終了時に簡単に取り外してカウンタの値を読み取ったり、別の値に変更したりできます。
そして、テスト後、デバイスはすでに新しい制御モジュールで組み立てられています。
おわりに
夕方に約1週間続いたこのデバイスでの作業の結果、私は、初心者でも電子機器を理解することさえ、最新のマイクロコントローラーでのデバイスの開発は難しくないと確信しました。 すべてが計画通りに機能したという事実、さらに燃え上がった熱意。 そして、手作業で行われた何かからかなりの喜びが得られました。
繰り返しになりますが、その前は、マイクロコントローラーのプログラミングや電子機器の経験はほとんどありませんでした。 したがって、ソフトウェアとハードウェアの両方で間違いを犯した可能性が高いため、厳密に判断しないようお願いします。
ご清聴ありがとうございました。