//******************************************************************** //* * //* RV-Project.Com * //* Magnetometer Camera Switch - XYZ Select Version * //* * //* Mar-25-2018 * //* * //* Use With Hardware: * //* MCS-1C Rev A * //* * //* This sketch is intended for use with Camera Switch boards. * //* This software is licensed by Creative Commons for non-commercial * //* use only, and must be used within that scope. * //* * //* Sketch works with Atmel ATTiny85-10 and above. ATTiny85 must be * //* set for 8Mhz internal clocking. If default 1Mhz internal clock * //* is used, time delay (on period) will not be accurate. * //* Works with HMC5983, HMC5883, and MAG3110 based magnetometers. * //* * //* Originally the board was designed with the HMC5983, but it soon * //* went out of production after the design was finalized. Therefore * //* it was found that the MAG3110, which is still in production is a * //* suitable substitute with the changes pointed out in the sketch. * //* * //* You can still get the HMC5983, and it is the preferred module. * //* Do a (search HMC5983 on eBay). However, make sure the breakout * //* board has the same pin-outs for pins 1 thru 4. The best boards * //*(so far) that seem to work are the GY-282 boards which do seem to * //* still be available, especially on eBay. * //* * //* Pin 1: VCC * //* Pin 2: GND * //* Pin 3: SCL * //* Pin 4: SDA * //* * //* Pins 5 and higher are not used on the board (even though there * //* is a pad on the circuit board for them. Some boards, especially * //* those from SparkFun or Adafruit have a different pinout. * //* * //* Also, make sure the breakout board works on 5V. Most of the * //* Magnetic Compass ICs run on 3.3V, and the breakout boards * //* typically have a 5V-3V regulator, as well as compensate for the * //* voltage differences in the data lines. Again, the exception is * //* Sparkfun/Adafruit as some of those boards allow you to use * //* either 5V or 3.3V to the board - hence the different pin-out * //* * //* Interference: The magnetometer may trip if a large ferrous metal * //* object passes by. This is because this material will change the * //* compass reading. However, this only happens if an object passes * //* by, not if the object is static. While it may be true that the * //* metal will cause a heading error, this switch works on a change * //* in the heading, not the heading itself. So not a problem as long * //* as the metal stays put. * //* * //* This is NOT true if a magnet or RF field is nearby, as both of * //* these can upset the local magnetic field during operation. For * //* that reason, do not drive a relay with this switch, and as well * //* ensure the camera is far enough away from the switch so it does * //* cause it to stay on. * //* * //* Lastly, most breakout boards have pull-up resistors onboard, so * //* you can omit R3 and R4 from the circuit board in those cases. * //* But if you are not sure, the device will still work if you use * //* R3/R4 on the circuit board. * //* * //* Note: There are some Shenzhen Electronic Marketplace "deal of * //* the day" boards on eBay, Amazon, Won Hng Lo, etc. that may * //* require a different I2C address and register settings to get * //* them to work. If you cannot get your board to work at all, make * //* sure you are using the correct board and registers (i.e. look in * //* the data sheet for the Magnetic Compass IC used with that board. * //* * //* Starting with this version (for MCS-1C boards), you can select * //* which Axis you want to read, or if you want to read/average all * //* three axis. This is accomplished with a Programming Header as * //* shown below. Note that the header is only read during SETUP, so * //* if you change the header, you must power down the board or * //* depress the reset button. * //* * //* Programming Header: ^ towards MPU * //* ----- * //* |* *| - Pin X (X Axis) * //* |* *| - Pin Y (Y Axis) * //* |* *| - Pin Z (Z Axis) * //* ----- * //* Configuration: * //* Pin X = 4.99V (1023) * //* Pin Y = 4.208V (876) * //* Pin Z = 3.252V (677) * //* Pin Y+Z = 3.942V (821) (combined X/Y/Z) * //* * //* Speaking of the reset button, you must also reset the board * //* after you make any changes to the sensitivity or delay potent- * //* iometers. This is because these controls are also read in Setup. * //* * //* I did this because the ATTiny85 is not really that powerful, and * //* it must process a lot of data from the Magnetic Compass. So by * //* alleviating the need to read the pin settings continuously, the * //* ATtiny85 can spend more time reading the compass output. * //* * //* This should not be a problem because the sensitivity, delay, and * //* axis inputs should not change often. Once you make any changes * //* to those inputs, simply depress the reset button. * //* * //* TinyWireM 1/21/2011 courtesy BroHogan - brohoganx10@gmail.com * //* * //******************************************************************** #include //I2C library for ATTiny // I2C board address // only one of the next three lines should be enabled! #define addr 0x1E //<--- "Genuine" HMC5983 ONLY //#define addr 0x0D //<--- HMC5883 ONLY //#define addr 0x0E //<--- MAG3110 ONLY //pin configuration //Pin 1 (Digital) Reset //Pin 2 (Analog) Delay Adjustment/ManualMode //Pin 3 (Analog) Sensitivity Adjustment //Pin 4 GND //Pin 5 (Serial Digital) hardwired to I2C-SDI/SDA //Pin 6 (Digital) Output to MOSFET //Pin 7 (Serial Digital) hardwired to I2C-SCK/SCL //Pin 8 VCC int OldAxis = 0; //temporary variable bool DirtyFlag = false; //dirty flag for on/off transition int sampleSize = 10; //Magnetometer output averaging =5 for MPU6050, 10 for all others int Offset = 3; //sensitivity adjustment int Axis = 0; //Current Axis (X, Y, Z, or ALL) //timer variables unsigned long previousMillis = 0; unsigned long interval = 0; unsigned long delayValue = 0; int CurAxis; //axis data int OutputPin = 1; //physical pin 6 void setup(){ //I2C Configuration TinyWireM.begin(); TinyWireM.beginTransmission(addr); //start talking //Init and Setup Registers for the Magnetic Compass boards. TinyWireM.write(0x02); // <--- HMC5983 ONLY TinyWireM.write(0x00); // <--- HMC5983 ONLY //TinyWireM.write(0x09); // <--- HMC5883 ONLY //TinyWireM.write(0x1D); // <--- HMC5883 ONLY //TinyWireM.write(0x11); // <--- MAG3110 ONLY //TinyWireM.write(0x80); // <--- MAG3110 ONLY //TinyWireM.endTransmission(); // <--- MAG3110 ONLY //delay(15); // <--- MAG3110 ONLY //TinyWireM.beginTransmission(addr); // <--- MAG3110 ONLY //TinyWireM.write(0x10); // <--- MAG3110 ONLY //TinyWireM.write(1); // <--- MAG3110 ONLY TinyWireM.endTransmission(); delay(1000); //ensures inputs stabilize before initial readings pinMode(OutputPin,OUTPUT); digitalWrite(OutputPin,LOW); //ensure board starts with output off. //Note that the following values are in setup. That allows fewer calculations //to be made during operation (the Magnetometer blasts the ATTiny85 with so //much data it helps to minimize the loop tasks. //after changing delay and sensitivity potentiometers, a reset or power cycle //is required to effect those changes. delayValue=analogRead(3); //read the delay potentiometer //the minimum value the ADC can output is between 120 and 200 - depending on the variation of the //tolerance of the resistor ladder, voltage regulator, and ATTiny85. This equates to around a //2 minute minimum. This is not helpful for testing, so if the potentiometer wiper is bottomed out, //(ADC value less than 200), then a test mode places a 1 second delay on the DelayValue. You might //think that simply changing the map() command below to less than 200 will accomplish the same thing //however, due to the tolerances mentioned above, this would have to be changed for every board built. //Therefore, this approch will provide a 1Sec "test" delay, when the wiper is at the bottom, and an //"operational" delay of about 2 minutes to 60 minutes when the wiper is advanced about 1/8th turn //towards the top. //approx ADC values 148-894 if (delayValue < 200) //Test Mode if Delay Value at minimum { delayValue = 1000; //If potentiometer is less than 200, use a 1 second } //delay for testing. Values are calculated for 8Mhz Clock. else //otherwise, use the delay set by the potentiometer { delayValue = map(delayValue,200,900,1,3600); //potentiometer setting range: 2 minutes = 200, 1 hour = 3600 //counterclockwise rotation increases delay. In actual practice //the delay is from 12Min to 60Min (except fo the 1 second test mode). interval = delayValue * 1000; //convert to mS } //Axis Selection //******************************************************************* int ReadAxis = 0; ReadAxis = analogRead(A0); if (ReadAxis > 949) Axis=0; //Axis X if ((ReadAxis <= 949) && (ReadAxis > 848)) Axis = 1; //Axis Y if ((ReadAxis <= 848) && (ReadAxis > 749)) Axis = 3; //Axis Y+Z = ALL Axis if (ReadAxis <= 749) Axis = 2; //Axis Z digitalWrite(OutputPin,LOW); //ensure board starts with output off. delay(1000); //set sensitivity //clockwise rotation decreases sensitivity. Offset = map(analogRead(2),0,1023,1,254); //Offset range 1-254 CurAxis = readAxis(); //set initial value to prevent turn on at beginning. OldAxis = CurAxis; //as well as OldAxis value. } //********************************* End Setup ******************************** void loop() { unsigned long currentMillis = millis(); CurAxis = readAxis(); //see if CurAxis has changed more than the Offset from the last reading. //if ((CurAxis - Offset) > OldAxis) DirtyFlag = true; //positive going change //if ((CurAxis + Offset) < OldAxis) DirtyFlag = true; //negative going change if (CurAxis > (OldAxis + Offset)) DirtyFlag = true; //positive going change if (CurAxis < (OldAxis - Offset)) DirtyFlag = true; //negative going change if(analogRead(3) < 120) DirtyFlag = true; //force on with ground on J2 Pin 4 if (DirtyFlag == true) //if the CurAxis has changed more than the Offset. { digitalWrite(OutputPin,HIGH); //turn the output MOSFET on. previousMillis = currentMillis; //start the time delay countdown. OldAxis = CurAxis; //update the current position value. DirtyFlag = false; //reset the Dirty Flag delay(500); //hysteresis } if(currentMillis - previousMillis > interval) //if the delay timer exceeds the last change (i.e. no movement) { digitalWrite(OutputPin,LOW); //turn the output MOSFET off. } } //********************************* End Loop ******************************** int readAxis() //***************************************************** //* READAXIS * //* * //* Reads input of the magnetometer * //* the number of times defined in sampleSize, then * //* return the average of the axis with the most * //* significant change. * //***************************************************** { long reading = 0; //Axis combining variables int xx = 0; int yy = 0; int zz = 0; //Axis accumulator variables int CurX = 0; int CurY = 0; int CurZ = 0; delay(1); // increasing sample size will average the readings. This is useful if the // module is a bit unstable. The default is 10. for(int i = 0; i < sampleSize; i++) { //Tell the HMC what register to begin writing data into TinyWireM.beginTransmission(addr); TinyWireM.write(0x03); // <--- HMC5983 ONLY //TinyWireM.write(0x00); // <--- HMC5883 ONLY //TinyWireM.write(0x01); // <--- MAG3110 ONLY TinyWireM.endTransmission(); //Read the data.. 2 bytes for each axis.. 6 total bytes //must read all of the data or the wrong axis will be selected. TinyWireM.requestFrom(addr, 6); if(6 <= TinyWireM.available()) { xx = TinyWireM.read()<<8; //MSB x xx |= TinyWireM.read(); //LSB x zz = TinyWireM.read()<<8; //MSB z zz |= TinyWireM.read(); //LSB z yy = TinyWireM.read()<<8; //MSB y yy |= TinyWireM.read(); //LSB y } //accumulate data for each cycle CurX += xx; CurY += yy; CurZ += zz; } //pick the reading with the most change (alleviates selecting x,y,z at installation. //Avoid summing X,Y, and Z axis. Attempting to do so will result in noise false triggering //of the output. //the most accuracy is to read a single axis only; X,Y, or Z. The axis you use is //dependant on the orientation of the board in your vehicle. // ************* Read selected Axis *************** //if(Axis == 0) reading = abs(CurX); //if(Axis == 1) reading = abs(CurY); //if(Axis == 2) reading = abs(CurZ); //if(Axis == 3) reading = abs(CurX + CurY + CurZ) / 3; if(Axis == 0) reading = CurX; if(Axis == 1) reading = CurY; if(Axis == 2) reading = CurZ; if(Axis == 3) reading = (CurX + CurY + CurZ) / 3; return reading/sampleSize; } //********************************* End ReadAxis ********************************