ブレーキでテールランプ点滅。追突の危険性を軽減!
防犯機能で車体を触られたときにキーを入力しないとアラーム。レーザで路面にラインも表示
こんな機能
折角趣味で作るのだから、欲しい機能を詰め込みました。
- テールランプを順に点滅(基本機能)
- ブレーキでテールランプが点滅。追突の危険性を軽減
- 防犯機能。車体を触られたときにキーを入力しないとアラーム
- 路面にレーザーライン
- 電池残量、加速度、現在温度表示(おまけ機能)
部品構成
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
投稿者の人気記事
-
tea
さんが
2022/03/10
に
編集
をしました。
(メッセージ: 初版)
-
tea
さんが
2022/03/23
に
編集
をしました。
-
tea
さんが
2022/04/19
に
編集
をしました。
-
tea
さんが
2022/08/14
に
編集
をしました。
ログインしてコメントを投稿する