diff --git a/sw/boitarire_sw/boitarire_sw.ino b/sw/boitarire_sw/boitarire_sw.ino index 09968bf25ccf64021c747490cd031d67c75e1c81..e0d4762a71bab6efb165bbbb38e82b44c9712746 100644 --- a/sw/boitarire_sw/boitarire_sw.ino +++ b/sw/boitarire_sw/boitarire_sw.ino @@ -41,13 +41,14 @@ //#define MP3_RESET // //#define DEBUG_UART_ACCELEROMETER // Warning : conflict with MP3 player -//#define BLANK_READ_BUSY // MP3 module BUSY signal is taken in account when blanking. If not defined, only the blanking value in the playlist is used -#define BLANK_DELAY_MIN 200 // Minimum blankig delay before checking BUSY, when BLANK_READ_BUSY is used +#define ACCEL_ROTATION_DETECT // When set with SENSOR_ACCELEROMETER, trigger is activated on rotation detection, instead of raw movement threshold #define DELAY_MP3_RESET 100 // Time of ground cut of MP3 module for re-init (ms) : Adjust only if hardware proiblems with new revisions of the mp3 module #define DEBOUNCE_DELAY 200 // Position sensor input debounce (ms) #define ACCEL_THRES_GYX 9999 // Detection threshold for X axis acceleration #define ACCEL_THRES_GYY 9999 // Detection threshold for Y axis acceleration -#define ACCEL_THRES_GYZ 25000 // Detection threshold for Z axis acceleration +#define ACCEL_THRES_GYZ 25000 // Detection threshold for Z axis acceleration +#define ACCEL_THRES_ROT 200 // Rotation threshold detection, for rotational speed integration pre-filtering +#define ACCEL_TRIG_ROT 2000 // Rotational distance to be traveled to trigger detection #define PLAYLIST_LENGTH 4 // Number of songs in the playlist // Typedefs @@ -83,21 +84,46 @@ typedef struct uint16_t blank_delay_u16; // Blanking time for the song (ms) } tSongItem; +/* Accelerometer sample structure + * Stores two samples, filtered value, integrated value + */ +typedef struct +{ + int16_t sample1_s16; + int16_t sample2_s16; + int16_t sample_filt_s16; + int16_t sample_int_s16; +} tAccelSample; + +/* Accelerometer samples + * Stores all samples for signal processing of accelerometer data + */ +typedef struct +{ + tAccelSample AcX; // X linear axis + tAccelSample AcY; // Y linear axis + tAccelSample AcZ; // Z linear axis + tAccelSample GyX; // X rotational axis + tAccelSample GyY; // Y rotational axis + tAccelSample GyZ; // Z rotational axis +} tAccelData; + // Constant variables /* Indifference table for songs cycles * Contains the indexes of the songs to be played and the number of times each one must be played. - * Contains couples {index of the song in the MP3 device memory, number of times the song must be played}, + * Contains couples {index of the song in the MP3 device memory, number of times the song must be played, blanking time}, * Playlist cycles from first element to last and loops. * Don't forget to update PLAYLIST_LENGTH define. */ const tSongItem cPlaylistIndif_TA[PLAYLIST_LENGTH] = { - {0, 2, 100}, - {1, 3, 100}, + {0, 2, 1000}, + {1, 2, 1000}, }; // Local variables + String inputString = ""; // a String to hold incoming data bool stringComplete = false; // whether the string is complete @@ -107,8 +133,10 @@ unsigned long play_blank_delay, play_blank_delay_threshold, mp3_reset_delay; unsigned long millis_temp; const int MPU=0x68; -int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; +int16_t Tmp; +tAccelData AccelData_T; bool accel_detect; +bool accel_detect_x, accel_detect_y, accel_detect_z; uint8_t song_cycles_cnt_u8; // Counts the number of times the current song has been played uint8_t song_playing_current_u8; // Holds the index of the playlist song actually being played @@ -180,6 +208,7 @@ void setup() { Wire.write(0x06); Wire.endTransmission(true); #endif + accel_detect = false; // Connect ground for MP3 module @@ -217,20 +246,111 @@ void loop() { Wire.write(0x3B); Wire.endTransmission(false); Wire.requestFrom(MPU,14,true); - AcX=Wire.read()<<8|Wire.read(); - AcY=Wire.read()<<8|Wire.read(); - AcZ=Wire.read()<<8|Wire.read(); - Tmp=Wire.read()<<8|Wire.read(); - GyX=Wire.read()<<8|Wire.read(); - GyY=Wire.read()<<8|Wire.read(); - GyZ=Wire.read()<<8|Wire.read(); - - if ((GyX > ACCEL_THRES_GYX) || - (GyY > ACCEL_THRES_GYY) || - (GyZ > ACCEL_THRES_GYZ) || - (GyX < ((int16_t)(-1) * ACCEL_THRES_GYX)) || - (GyY < ((int16_t)(-1) * ACCEL_THRES_GYY)) || - (GyZ < ((int16_t)(-1) * ACCEL_THRES_GYZ))) + AccelData_T.AcX.sample1_s16=Wire.read()<<8|Wire.read(); + AccelData_T.AcY.sample1_s16=Wire.read()<<8|Wire.read(); + AccelData_T.AcZ.sample1_s16=Wire.read()<<8|Wire.read(); + Tmp=Wire.read()<<8|Wire.read(); + AccelData_T.GyX.sample1_s16=Wire.read()<<8|Wire.read(); + AccelData_T.GyY.sample1_s16=Wire.read()<<8|Wire.read(); + AccelData_T.GyZ.sample1_s16=Wire.read()<<8|Wire.read(); + +#ifdef ACCEL_ROTATION_DETECT +/* + * Rotation detection + * Detection is done independantely on each axis. + * On each axis, Gy... input is filtered high-pass in order to filter the idle / background noise. + * High(pass filter is standard first order IIR with alpha = 0.5. + * If the high-pass filtered data reaches the ACCEL_THRES_ROT threshold, or if the unfiltered data reaches 2^4 times ACCEL_THRES_ROT, + * the data is then considered valid and integrated. + * Once valid, the input measure is added to the integrated data. + * Once the integrated data reaches ACCEL_TRIG_ROT, a trig is launched on this axis. + * A trigger occurs if any of the three axis is triggered. + */ + + // X axis processing ----- + // GyX high-pass filtering + AccelData_T.GyX.sample_filt_s16 = (AccelData_T.GyX.sample_filt_s16 + (AccelData_T.GyX.sample1_s16 - AccelData_T.GyX.sample2_s16)) >> 1; + if ((AccelData_T.GyX.sample1_s16 > (ACCEL_THRES_ROT << 4)) || + (AccelData_T.GyX.sample1_s16 < -(ACCEL_THRES_ROT << 4)) || + (AccelData_T.GyX.sample_filt_s16 > ACCEL_THRES_ROT) || + (AccelData_T.GyX.sample_filt_s16 < -(ACCEL_THRES_ROT))) // Threshold for movement detection + { + AccelData_T.GyX.sample_int_s16 += (AccelData_T.GyX.sample1_s16 >> 9); // Integration + } + + if ((AccelData_T.GyX.sample_int_s16 > ACCEL_TRIG_ROT) || + (AccelData_T.GyX.sample_int_s16 < -(ACCEL_TRIG_ROT))) + { + AccelData_T.GyX.sample_int_s16 = 0; // Reset integrator + accel_detect_x = true; + } + else + { + accel_detect_x = false; + } + + AccelData_T.GyX.sample2_s16=AccelData_T.GyX.sample1_s16; // N-1 sample memorisation + + // Y Axis processing ----- + // GyY high-pass filtering + AccelData_T.GyY.sample_filt_s16 = (AccelData_T.GyY.sample_filt_s16 + (AccelData_T.GyY.sample1_s16 - AccelData_T.GyY.sample2_s16)) >> 1; + + if ((AccelData_T.GyY.sample1_s16 > (ACCEL_THRES_ROT << 4)) || + (AccelData_T.GyY.sample1_s16 < -(ACCEL_THRES_ROT << 4)) || + (AccelData_T.GyY.sample_filt_s16 > ACCEL_THRES_ROT) || + (AccelData_T.GyY.sample_filt_s16 < -(ACCEL_THRES_ROT))) // Threshold for movement detection + { + AccelData_T.GyY.sample_int_s16 += (AccelData_T.GyY.sample1_s16 >> 9); // Integration + } + + if ((AccelData_T.GyY.sample_int_s16 > ACCEL_TRIG_ROT) || + (AccelData_T.GyY.sample_int_s16 < -(ACCEL_TRIG_ROT))) + { + AccelData_T.GyY.sample_int_s16 = 0; // Reset integrator + accel_detect_y = true; + } + else + { + accel_detect_y = false; + } + + AccelData_T.GyY.sample2_s16=AccelData_T.GyY.sample1_s16; // N-1 sample memorisation + + // Z Axis processing ----- + // GyZ high-pass filtering + AccelData_T.GyZ.sample_filt_s16 = (AccelData_T.GyZ.sample_filt_s16 + (AccelData_T.GyZ.sample1_s16 - AccelData_T.GyZ.sample2_s16)) >> 1; + + if ((AccelData_T.GyZ.sample1_s16 > (ACCEL_THRES_ROT << 4)) || + (AccelData_T.GyZ.sample1_s16 < -(ACCEL_THRES_ROT << 4)) || + (AccelData_T.GyZ.sample_filt_s16 > ACCEL_THRES_ROT) || + (AccelData_T.GyZ.sample_filt_s16 < -(ACCEL_THRES_ROT))) // Threshold for movement detection + { + AccelData_T.GyZ.sample_int_s16 += (AccelData_T.GyZ.sample1_s16 >> 9); // Integration + } + + if ((AccelData_T.GyZ.sample_int_s16 > ACCEL_TRIG_ROT) || + (AccelData_T.GyZ.sample_int_s16 < -(ACCEL_TRIG_ROT))) + { + AccelData_T.GyZ.sample_int_s16 = 0; // Reset integrator + accel_detect_z = true; + } + else + { + accel_detect_z = false; + } + + AccelData_T.GyZ.sample2_s16=AccelData_T.GyZ.sample1_s16; // N-1 sample memorisation + + // Concatenation of the three axis + accel_detect = accel_detect_x || accel_detect_y ||accel_detect_z; + +#else //ACCEL_ROTATION_DETECT + if ((AccelData_T.GyX.sample1_s16 > ACCEL_THRES_GYX) || + (AccelData_T.GyY.sample1_s16 > ACCEL_THRES_GYY) || + (AccelData_T.GyZ.sample1_s16 > ACCEL_THRES_GYZ) || + (AccelData_T.GyX.sample1_s16 < ((int16_t)(-1) * ACCEL_THRES_GYX)) || + (AccelData_T.GyY.sample1_s16 < ((int16_t)(-1) * ACCEL_THRES_GYY)) || + (AccelData_T.GyZ.sample1_s16 < ((int16_t)(-1) * ACCEL_THRES_GYZ))) { accel_detect = true; } @@ -238,16 +358,17 @@ void loop() { { accel_detect = false; } -#else +#endif //ACCEL_ROTATION_DETECT +#else //SENSOR_ACCELEROMETER accel_detect = false; -#endif +#endif //SENSOR_ACCELEROMETER #ifdef SENSOR_INCLINATION // Read instantaneous value of position sensor input_position_sensor = digitalRead(PIN_SENSOR_POSITION); -#else +#else //SENSOR_INCLINATION input_position_sensor = LOW; -#endif +#endif //SENSOR_INCLINATION // Inclination debounce state machine //----------------------------------- @@ -349,7 +470,7 @@ void loop() { case STM_PLAY: WritePlaySong(cPlaylistIndif_TA[song_playing_current_u8].song_index_u8 + 1U); // Play the current song - digitalWrite(LED_BUILTIN, HIGH); // DEBUG + //digitalWrite(LED_BUILTIN, HIGH); // DEBUG play_blank_delay_threshold = cPlaylistIndif_TA[song_playing_current_u8].blank_delay_u16; // Load blankig time for the current song song_cycles_cnt_u8++; // Increment cycles counter @@ -407,18 +528,24 @@ void loop() { } #ifdef DEBUG_UART_ACCELEROMETER - Serial.print("Accelerometer: "); - Serial.print("X = "); Serial.print(AcX); - Serial.print(" | Y = "); Serial.print(AcY); - Serial.print(" | Z = "); Serial.println(AcZ); + // Format : https://diyrobocars.com/2020/05/04/arduino-serial-plotter-the-missing-manual/ + // Adapted for serial plotter + // //Serial.print("Accelerometer: "); + //Serial.print("X:"); Serial.print(AccelData_T.AcX.sample1_s16);Serial.print(","); + //Serial.print("Y:"); Serial.print(AccelData_T.AcY.sample1_s16);Serial.print(","); + //Serial.print("Z:"); Serial.print(AccelData_T.AcZ.sample1_s16);Serial.print(","); - Serial.print("Gyroscope: "); - Serial.print("X = "); Serial.print(GyX); - Serial.print(" | Y = "); Serial.print(GyY); - Serial.print(" | Z = "); Serial.println(GyZ); - - Serial.print("Temperature: "); Serial.print(Tmp); - Serial.println(" "); + //Serial.print("Gyroscope: "); + //Serial.print("GX:"); Serial.print(AccelData_T.GyX.sample1_s16);Serial.print(","); + //Serial.print("GY:"); Serial.print(AccelData_T.GyY.sample1_s16);Serial.print(","); + //Serial.print("GZ:"); Serial.print(AccelData_T.GyZ.sample1_s16);Serial.print(","); + //Serial.print("GXfilt:"); Serial.print(AccelData_T.GyX.sample_filt_s16);Serial.print(","); + Serial.print("GXint:"); Serial.print(AccelData_T.GyX.sample_int_s16);Serial.print(","); + Serial.print("GYint:"); Serial.print(AccelData_T.GyY.sample_int_s16);Serial.print(","); + Serial.print("GZint:"); Serial.print(AccelData_T.GyZ.sample_int_s16);Serial.print(","); + + Serial.print("Temp:"); Serial.println(Tmp); + //Serial.println(" "); #endif }