teaのアイコン画像
tea 2022年03月10日作成 (2022年08月14日更新) © MIT
製作品 製作品 閲覧数 662
tea 2022年03月10日作成 (2022年08月14日更新) © MIT 製作品 製作品 閲覧数 662

多機能!自転車用テールランプ 衝突防止 防犯機能付き

ブレーキでテールランプ点滅。追突の危険性を軽減!
防犯機能で車体を触られたときにキーを入力しないとアラーム。レーザで路面にラインも表示

キャプションを入力できます

こんな機能

折角趣味で作るのだから、欲しい機能を詰め込みました。

  • テールランプを順に点滅(基本機能)
  • ブレーキでテールランプが点滅。追突の危険性を軽減
  • 防犯機能。車体を触られたときにキーを入力しないとアラーム
  • 路面にレーザーライン
  • 電池残量、加速度、現在温度表示(おまけ機能)

部品構成

Arduino Nanoと加速度センサーを使用して制御しています
その他部品はOLEDディスプレイ、レーザー×2、LED×5、スイッチ、ブザー、電池ボックスを使用しています

外はカリッと3Dプリンタで作成
中はグチャと配線だらけ

回路図

機能の説明

  • 急減速した場合、テールランプが点滅。追突の危険性を軽減
    加速度センサーで減速を感知した場合、LEDを点滅させます

  • 防犯機能。車体を触られたときにキーを入力しないとアラーム。
    静止状態が続いたときアラームモードに入ります。
    その状態で、車体が動いたときにキー入力をしてあげないとアラームがなります。
    いたずらしている人は正しいキー入力が出来ないため、アラームを止めることができません。

  • 路面にレーザーライン
    安物点レーザーを線レーザーに変えています。
    透明丸棒に点レーザーを当てれば、線レーザーに変わります。
    ガラス棒でもあれば良かったのですが高いので、透明なLEDを横にして代用しています

全コード

#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <avr/sleep.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 32 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) // The pins for I2C are defined by the Wire-library. // On an arduino UNO: A4(SDA), A5(SCL) // On an arduino MEGA 2560: 20(SDA), 21(SCL) // On an arduino LEONARDO: 2(SDA), 3(SCL), ... #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // MPU-6050のアドレス、レジスタ設定値 #define MPU6050_WHO_AM_I 0x75 // Read Only #define MPU6050_PWR_MGMT_1 0x6B // Read and Write #define MPU_ADDRESS 0x68 int LED_PIN1 = 4; // 変数に4を割り当てる(デジタルピン4番) int LED_PIN2 = 7; // センター int LED_PIN3 = 9; // 変数に7を割り当てる(デジタルピン7番) int LASER_PIN = 10; int BEEP_PIN = 12; // 変数に7を割り当てる(デジタルピン7番) int DISP_PIN = 3; // 変数に7を割り当てる(デジタルピン7番) int wakePin = 5; //割り込み用のピン番号 int wakePin2 = 6; //割り込み用のピン番号 bool active; float acc_old_z; float acc_old_y; int keyCount; bool keyFlag; int keyWait; // デバイス初期化時に実行される void setup() { Serial.println("Start"); pinMode(wakePin, INPUT_PULLUP); pinMode(wakePin2, INPUT_PULLUP); pinMode(A0, INPUT); Wire.begin(); // PCとの通信を開始 Serial.begin(115200); //115200bps // 初回の読み出し Wire.beginTransmission(MPU_ADDRESS); Wire.write(MPU6050_WHO_AM_I); //MPU6050_PWR_MGMT_1 Wire.write(0x00); Wire.endTransmission(); // 動作モードの読み出し Wire.beginTransmission(MPU_ADDRESS); Wire.write(MPU6050_PWR_MGMT_1); //MPU6050_PWR_MGMT_1レジスタの設定 Wire.write(0x00); Wire.endTransmission(); //ここから文字表示 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 Serial.println(F("SSD1306 allocation failed")); for(;;); // Don't proceed, loop forever } Serial.println("Start"); //BLINK pinMode(LED_BUILTIN, OUTPUT); pinMode(LED_PIN1, OUTPUT); // デジタルピン4番を出力モードに設定 pinMode(LED_PIN2, OUTPUT); // デジタルピン7番を出力モードに設定 pinMode(LED_PIN3, OUTPUT); // デジタルピン7番を出力モードに設定 pinMode(LASER_PIN, OUTPUT); // デジタルピン7番を出力モードに設定 pinMode(BEEP_PIN, OUTPUT); // デジタルピン7番を出力モードに設定 pinMode(DISP_PIN, OUTPUT); // デジタルピン7番を出力モードに設定 digitalWrite(DISP_PIN, HIGH); // turn the LED off by making the voltage LOW digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW active = true; keyFlag = true; } float old1; float old2; float old3; float old4; float blinkTime; int count; int blinkCount; int sindouCount; int sindouSinpuku; int sleepCount; int renCount; void loop() { if(!active){ //set_sleep_mode(SLEEP_MODE_PWR_DOWN); //スリープモードの設定 //interrupt(); delayWDT2(70); } Wire.beginTransmission(0x68); Wire.write(0x3B); Wire.endTransmission(false); Wire.requestFrom(0x68, 14, true); while (Wire.available() < 14); int16_t axRaw, ayRaw, azRaw, gxRaw, gyRaw, gzRaw, Temperature; axRaw = Wire.read() << 8 | Wire.read(); ayRaw = Wire.read() << 8 | Wire.read(); azRaw = Wire.read() << 8 | Wire.read(); Temperature = Wire.read() << 8 | Wire.read(); gxRaw = Wire.read() << 8 | Wire.read(); gyRaw = Wire.read() << 8 | Wire.read(); gzRaw = Wire.read() << 8 | Wire.read(); // 加速度値を分解能で割って加速度(G)に変換する float acc_x = axRaw / 16384.0; //FS_SEL_0 16,384 LSB / g float acc_y = ayRaw / 16384.0; float acc_z = azRaw / 16384.0; // 角速度値を分解能で割って角速度(degrees per sec)に変換する float gyro_x = gxRaw / 131.0;//FS_SEL_0 131 LSB / (°/s) float gyro_y = gyRaw / 131.0; float gyro_z = gzRaw / 131.0; if(!digitalRead(wakePin)){ Serial.print("Key "); active = true; sleepCount = 0; if(keyCount == 1){ keyFlag = true; } keyCount = 0; } if(!digitalRead(wakePin2)){ Serial.print("Key2 "); active = true; sleepCount = 0; if(keyCount == 0){ keyCount = 1; } } bool flag = false; if(acc_z >= acc_old_z -0.02 && acc_z <= acc_old_z +0.02 && acc_y >= acc_old_y -0.02 && acc_y <= acc_old_y +0.02){ flag = true; } if(flag){ //動いていない場合 //Serial.print("no active "); //Serial.print(sleepCount); //Serial.print(" "); //Serial.print(acc_y); Serial.println(""); if(sleepCount > 400){ display.clearDisplay(); display.display(); digitalWrite(LED_PIN1, LOW); digitalWrite(LED_PIN2, LOW); digitalWrite(LED_PIN3, LOW); digitalWrite(LASER_PIN, LOW); digitalWrite(DISP_PIN, LOW); active = false; acc_old_z = acc_z; acc_old_y = acc_y; keyFlag = false; keyWait = 0; return; } sleepCount++; } else{ digitalWrite(DISP_PIN, HIGH); //Serial.print("active "); active = true; sleepCount = 0; } acc_old_z = acc_z; acc_old_y = acc_y; //キーが入っていない場合ブザー if(!keyFlag && keyWait > 500){ digitalWrite(BEEP_PIN, HIGH); digitalWrite(LED_PIN1, LOW); digitalWrite(LED_PIN2, LOW); digitalWrite(LED_PIN3, LOW); delay(170); digitalWrite(BEEP_PIN, LOW); digitalWrite(LED_PIN1, HIGH); digitalWrite(LED_PIN2, HIGH); digitalWrite(LED_PIN3, HIGH); delay(120); sleepCount+=10; } keyWait++; // Serial.print((Temperature+521)/340+35); Serial.print(","); //Serial.print(acc_x); Serial.println("");//上下 Serial.print(acc_z); Serial.println("");//傾き //Serial.print(acc_y); Serial.println("");//前後 //Serial.print(gyro_x); Serial.print(","); //Serial.print(gyro_y); Serial.print(","); //Serial.print(gyro_z); Serial.println(""); float sa = 0.12; float heikinOld = (old4*0.5 +old3*0.3 +old2*0.2); float heikinNew = (acc_y +old1)/2; //振動 if(heikinOld < heikinNew -sa && sindouSinpuku != 1){ sindouCount+=4; sindouSinpuku = 1; } if(heikinOld > heikinNew +sa && sindouSinpuku != -1){ sindouCount+=4; sindouSinpuku = -1; } //振動以外で減速したとき if(heikinOld < heikinNew -sa && sindouCount < 2){ blinkTime = 28; } sindouCount--; if(sindouCount < 0){ sindouCount = 0; sindouSinpuku = 0; } if(sindouCount > 10){ sindouCount = 10; } old4 = old3; old3 = old2; old2 = old1; old1 = acc_y; digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW display.clearDisplay(); display.setTextSize(2); // Draw 2X-scale text display.setCursor(0,0); // Start at top-left corner display.setTextColor(SSD1306_WHITE); display.print((Temperature+521)/340+35); display.print("C "); display.print(cpuVcc()); // 電源電圧測定 http://radiopench.blog96.fc2.com/blog-entry-490.html //display.print(analogRead(A0)); display.println("V"); if(keyFlag){ display.print("KEY "); } else{ display.print("INPUT "); display.print((1000 -keyWait)/10); } display.drawLine(display.width()/2, display.height() -2, display.width()/2 +heikinNew*100, display.height() -2, SSD1306_WHITE); display.drawLine(display.width()/2, display.height() -4, display.width()/2 +(heikinNew-heikinOld)*200, display.height() -4, SSD1306_WHITE); display.drawLine(display.width()/2, display.height() -6, display.width()/2 +sa*200, display.height() -6, SSD1306_WHITE); if(blinkTime > 0){ //display.println(heikinNew*100); display.println("brake"); //digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) if(count%2 == 0){ digitalWrite(LED_PIN1, LOW); digitalWrite(LED_PIN2, HIGH); digitalWrite(LED_PIN3, LOW); digitalWrite(LASER_PIN, HIGH); } else{ digitalWrite(LED_PIN1, LOW); digitalWrite(LED_PIN2, LOW); digitalWrite(LED_PIN3, LOW); digitalWrite(LASER_PIN, LOW); } blinkTime--; } else{ if(count%8 == 0){ blinkCount++; switch(blinkCount%3){ case 0: digitalWrite(LED_PIN1, HIGH); // デジタルピン4番に繋いだLEDを点灯 digitalWrite(LED_PIN2, HIGH); // デジタルピン7番に繋いだLEDを消灯 digitalWrite(LED_PIN3, HIGH); // デジタルピン7番に繋いだLEDを消灯 digitalWrite(LASER_PIN, HIGH); break; case 1: digitalWrite(LED_PIN1, HIGH); // デジタルピン4番に繋いだLEDを点灯 digitalWrite(LED_PIN2, HIGH); // デジタルピン7番に繋いだLEDを消灯 digitalWrite(LED_PIN3, LOW); // デジタルピン7番に繋いだLEDを消灯 digitalWrite(LASER_PIN, LOW); break; case 2://全点灯 digitalWrite(LED_PIN1, LOW); // デジタルピン4番に繋いだLEDを点灯 digitalWrite(LED_PIN2, HIGH); // デジタルピン7番に繋いだLEDを消灯 digitalWrite(LED_PIN3, LOW); // デジタルピン7番に繋いだLEDを消灯 digitalWrite(LASER_PIN, LOW); break; } } } count++; display.display(); delay(20); } //割込み関数 void interrupt(){ //attachInterrupt(0,wakeUp, LOW); //割り込み処理の設定、2番ピンがLowで復帰 PRR = PRR | 0b00100000; //8bitタイマーdisable sleep_mode(); //スリープモードを有効にする //detachInterrupt(0); //割り込み処理を解除する } void wakeUp(){ //Serial.println("Wake up from standby."); //復帰時にシリアルモニターに表示 PRR = PRR & 0b00000000; //8bitタイマーenable //delay(30000); //時間調整 } void delayWDT2(unsigned long t) { // パワーダウンモードでdelayを実行 Serial.flush(); // シリアルバッファが空になるまで待つ delayWDT_setup(t); // ウォッチドッグタイマー割り込み条件設定 // ADCを停止(消費電流 147→27μA) ADCSRA &= ~(1 << ADEN); set_sleep_mode(SLEEP_MODE_PWR_DOWN); // パワーダウンモード指定 sleep_enable(); // BODを停止(消費電流 27→6.5μA) MCUCR |= (1 << BODSE) | (1 << BODS); // MCUCRのBODSとBODSEに1をセット MCUCR = (MCUCR & ~(1 << BODSE)) | (1 << BODS); // すぐに(4クロック以内)BODSSEを0, BODSを1に設定 asm("sleep"); // 3クロック以内にスリープ sleep_mode();では間に合わなかった sleep_disable(); // WDTがタイムアップでここから動作再開 ADCSRA |= (1 << ADEN); // ADCの電源をON(BODはハードウエアで自動再開される) } void delayWDT_setup(unsigned int ii) { // ウォッチドッグタイマーをセット。 // 引数はWDTCSRにセットするWDP0-WDP3の値。設定値と動作時間は概略下記 // 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms // 6=1sec, 7=2sec, 8=4sec, 9=8sec byte bb; if (ii > 9 ) { // 変な値を排除 ii = 9; } bb = ii & 7; // 下位3ビットをbbに if (ii > 7) { // 7以上(7.8,9)なら bb |= (1 << 5); // bbの5ビット目(WDP3)を1にする } bb |= ( 1 << WDCE ); MCUSR &= ~(1 << WDRF); // MCU Status Reg. Watchdog Reset Flag ->0 // start timed sequence WDTCSR |= (1 << WDCE) | (1 << WDE); // ウォッチドッグ変更許可(WDCEは4サイクルで自動リセット) // set new watchdog timeout value WDTCSR = bb; // 制御レジスタを設定 WDTCSR |= _BV(WDIE); } ISR(WDT_vect) { // WDTがタイムアップした時に実行される処理 // wdt_cycle++; // 必要ならコメントアウトを外す } float cpuVcc(){ // 電源電圧(AVCC)測定関数 long sum=0; adcSetup(0x4E); // Vref=AVcc, input=internal1.1V for(int n=0; n < 10; n++){ sum = sum + adc(); // adcの値を読んで積分 } return (1.1 * 10240.0)/ sum; // 電圧を計算して戻り値にする } void adcSetup(byte data){ // ADコンバーターの設定 ADMUX = data; // ADC Multiplexer Select Reg. ADCSRA |= ( 1 << ADEN); // ADC イネーブル ADCSRA |= 0x07; // AD変換クロック CK/128 delay(10); // 安定するまで待つ } unsigned int adc(){ // ADCの値を読む unsigned int dL, dH; ADCSRA |= ( 1 << ADSC); // AD変換開始 while(ADCSRA & ( 1 << ADSC) ){ // 変換完了待ち } dL = ADCL; // LSB側読み出し dH = ADCH; // MSB側 return dL | (dH << 8); // 10ビットに合成した値を返す } void testfillcircle(void) { for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) { // The INVERSE color is used so circles alternate white/black display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE); } }
1
ログインしてコメントを投稿する