Home › Forums › Mayfly Data Logger › Using two MB7389s via Grove
- This topic has 4 replies, 2 voices, and was last updated 2018-08-23 at 11:48 AM by CECL.
-
AuthorPosts
-
-
2018-08-20 at 5:52 PM #12354
I’m attempting to use two MB7389 sonars on one Mayfly. My code works if I connect one sonar via grove to D10/11 and the other to D18/19 on the digital 20-pin header (see code below, derived mostly from the example sonar sketch). However, I would like to attach both sonars to the Mayfly via grove cables (e.g. D10/11 and D6/7) but the board crashes and loops the set-up endlessly if I change the numbers at lines 24 and 26 to 6 and 7. I’d greatly appreciate any help getting two grove connections working.
Arduino123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454// v.4 Uses 2 sonar sensors, one for changes in bed elevation and another for water elevation// Bed-Level Sensor// Sonar 7 to Mayfly Grnd// Sonar 6 to Mayfly D18// Sonar 5 to Mayfly D19// Water-Level Sensor// Sonar 7 to Mayfly Grnd// Sonar 6 to Mayfly D10// Sonar 5 to Mayfly D11#include <Wire.h>#include <avr/sleep.h>#include <avr/wdt.h>#include <SPI.h>#include <SD.h>#include <RTCTimer.h>#include <Sodaq_DS3231.h>#include <Sodaq_PcInt_PCINT0.h>#include <SoftwareSerial_PCINT12.h>const int SonarExcite_Bed = 18;const int SonarExcite_Water = 10;SoftwareSerial sonarSerial_Bed(19, -1);SoftwareSerial sonarSerial_Water(11, -1);boolean stringComplete_Bed = false;boolean stringComplete_Water = false;#define READ_DELAY 1//RTC TimerRTCTimer timer;String dataRec = "";int currentminute;int currentsecond;long currentunixtime = 0;float boardtemp = 0.0;int batteryPin = A6; // select the input pin for the potentiometerint batterysenseValue = 0; // variable to store the value coming from the sensorfloat batteryvoltage;float batterypercent;int range_mm_Bed;int range_mm_Water;//RTC Interrupt pin#define RTC_PIN A7#define RTC_INT_PERIOD EveryMinute#define SD_SS_PIN 12//The data log file#define FILE_NAME "DataLog.txt"///////////////////////////// SET DATA HEADER /////////////////////////////#define LOGGERNAME "Station [1]"#define PROJECT_LOCATION "Project & Location: Test"#define INSTALL_DATE "Installation Date: 19 August 2018"#define DATA_HEADER "DateTime,UnixTime,BoardTemp_C,Battery_V,Battery_%,SonarRange_Bed_mm,SonarRange_Water_mm"///////////////////////////////////////////////////////////////////////////void setup(){//Initialise the serial connectionSerial.begin(57600);sonarSerial_Bed.begin(9600);sonarSerial_Water.begin(9600);rtc.begin();delay(100);pinMode(8, OUTPUT);pinMode(9, OUTPUT);//BedpinMode(SonarExcite_Bed, OUTPUT);digitalWrite(SonarExcite_Bed, LOW); //Power pin for the ultrasonic sensor//WaterpinMode(SonarExcite_Water, OUTPUT);digitalWrite(SonarExcite_Water, LOW); //Power pin for the ultrasonic sensorgreenred4flash(); //blink the LEDs to show the board is onsetupLogFile();//Setup timer eventssetupTimer();//Setup sleep modesetupSleep();Serial.println("Power On, running");Serial.println("Date,Time,UnixTime,BoardTemp_C,Battery_V,Battery_%,SonarRange_Bed_mm,SonarRange_Water_mm");}void loop(){//Update the timertimer.update();if(currentminute % 1 == 0) //Sample Frequency{digitalWrite(9, HIGH); // Red LED on while taking samplesdataRec = createDataRecord();//////////////// Bed ////////////////delay(200);digitalWrite(SonarExcite_Bed, HIGH);delay(400);range_mm_Bed = SonarRead_Bed();digitalWrite(SonarExcite_Bed, LOW);stringComplete_Bed = false;//////////////// Water ////////////////delay(200);digitalWrite(SonarExcite_Water, HIGH);delay(400);range_mm_Water = SonarRead_Water();digitalWrite(SonarExcite_Water, LOW);stringComplete_Water = false;///////////////////////////////////////////Save the data record to the log filelogData(dataRec);//Echo the data to the serial connectionSerial.println();Serial.print("Data Record: ");Serial.println(dataRec);String dataRec = "";digitalWrite(9, LOW); // Turn off Red LED when done taking samplesgreenflash(); // Green LED to notify successful samples takendelay(200);}systemSleep();}void showTime(uint32_t ts){//Retrieve and display the current date/timeString dateTime = getDateTime();//Serial.println(dateTime);}void setupTimer(){//Schedule the wakeup every minutetimer.every(READ_DELAY, showTime);//Instruct the RTCTimer how to get the current time readingtimer.setNowCallback(getNow);}void wakeISR(){//Leave this blank}void setupSleep(){pinMode(RTC_PIN, INPUT_PULLUP);PcInt::attachInterrupt(RTC_PIN, wakeISR);//Setup the RTC in interrupt modertc.enableInterrupts(RTC_INT_PERIOD);//Set the sleep modeset_sleep_mode(SLEEP_MODE_PWR_DOWN);}void systemSleep(){//Wait until the serial ports have finished transmittingSerial.flush();Serial1.flush();//The next timed interrupt will not be sent until this is clearedrtc.clearINTStatus();//Disable ADCADCSRA &= ~_BV(ADEN);//Sleep timenoInterrupts();sleep_enable();interrupts();sleep_cpu();sleep_disable();//Enbale ADCADCSRA |= _BV(ADEN);}String getDateTime(){String dateTimeStr;//Create a DateTime object from the current timeDateTime dt(rtc.makeDateTime(rtc.now().getEpoch()));currentunixtime = (dt.get()) + 946684800; //Unix time in secondscurrentminute = (dt.minute());currentsecond = (dt.second());//Convert it to a Stringdt.addToString(dateTimeStr);return dateTimeStr;}uint32_t getNow(){currentunixtime = rtc.now().getEpoch() + 946684800; // Converted to UnixTimereturn currentunixtime;}void greenred4flash(){for (int i=1; i <= 4; i++){digitalWrite(8, HIGH);digitalWrite(9, LOW);delay(50);digitalWrite(8, LOW);digitalWrite(9, HIGH);delay(50);}digitalWrite(9, LOW);}void greenflash(){digitalWrite(8, HIGH);delay(150);digitalWrite(8, LOW);delay(150);digitalWrite(8, HIGH);delay(150);digitalWrite(8, LOW);delay(150);}void setupLogFile(){//Initialise the SD cardif (!SD.begin(SD_SS_PIN)){Serial.println("Error: SD card failed to initialise or is missing.");//Hang// while (true);}//Check if the file already existsbool oldFile = SD.exists(FILE_NAME);//Open the file in write modeFile logFile = SD.open(FILE_NAME, FILE_WRITE);//Add header information if the file did not already existif (!oldFile){logFile.println(LOGGERNAME);logFile.println(PROJECT_LOCATION);logFile.println(INSTALL_DATE);logFile.println(DATA_HEADER);}//Close the file to save itlogFile.close();}void logData(String rec){//Re-open the fileFile logFile = SD.open(FILE_NAME, FILE_WRITE);//Write the CSV datalogFile.println(rec);//Close the file to save itlogFile.close();}String createDataRecord(){//Create a String type data record in csv format//Date, Time, UnixTime, Boardtemp, BatteryV, Battery%, Distance_Bed, Distance_WaterString data = getDateTime();data += ",";rtc.convertTemperature(); //convert current temperature into registersboardtemp = rtc.getTemperature(); //Read temperature sensor valuebatterysenseValue = analogRead(batteryPin);batteryvoltage = (3.3/1023.) * 4.7 * batterysenseValue;// Estimate battery percentage based on voltageif (batteryvoltage > 4.18){batterypercent = 100;}else if (batteryvoltage <= 4.18 && batteryvoltage > 4.0){batterypercent = 98;}else if (batteryvoltage <= 4.0 && batteryvoltage > 3.85){batterypercent = 95;}else if (batteryvoltage <= 3.85 && batteryvoltage > 3.75){batterypercent = 90;}else if (batteryvoltage <= 3.75 && batteryvoltage > 3.67){batterypercent = 80;}else if (batteryvoltage <= 3.67 && batteryvoltage > 3.62){batterypercent = 70;}else if (batteryvoltage <= 3.62 && batteryvoltage > 3.57){batterypercent = 60;}else if (batteryvoltage <= 3.57 && batteryvoltage > 3.52){batterypercent = 50;}else if (batteryvoltage <= 3.52 && batteryvoltage > 3.47){batterypercent = 40;}else if (batteryvoltage <= 3.47 && batteryvoltage > 3.42){batterypercent = 30;}else if (batteryvoltage <= 3.42 && batteryvoltage > 3.25){batterypercent = 20;}else if (batteryvoltage <= 3.25 && batteryvoltage > 3.1){batterypercent = 10;}else{batterypercent = 0;}data += currentunixtime;data += ",";addFloatToString(data, boardtemp, 3, 1); //floatdata += ",";addFloatToString(data, batteryvoltage, 4, 2);data += ",";addFloatToString(data, batterypercent, 4, 2);return data;}static void addFloatToString(String & str, float val, char width, unsigned char precision){char buffer[10];dtostrf(val, width, precision, buffer);str += buffer;}int SonarRead_Bed(){int result_Bed;char inData_Bed[5]; //char array to read data intoint index_bed = 0;sonarSerial_Bed.listen();while (sonarSerial_Bed.read() != -1) {}while (stringComplete_Bed == false) {if (sonarSerial_Bed.available()){char rByte = sonarSerial_Bed.read(); //read serial input for "R" to mark start of dataif(rByte == 'R'){//Serial.println("rByte set");while (index_bed < 4) //read next three character for range from sensor{if (sonarSerial_Bed.available()){inData_Bed[index_bed] = sonarSerial_Bed.read();//Serial.println(inData_Bed[index_bed]); //Debug lineindex_bed++; // Increment where to write next}}inData_Bed[index_bed] = 0x00; //add a padding byte at end for atoi() function}rByte = 0; //reset the rByte ready for next readingindex_bed = 0; // Reset index ready for next readingstringComplete_Bed = true; // Set completion of read to trueresult_Bed = atoi(inData_Bed); // Changes string data into an integer for use}}dataRec += ",";dataRec += result_Bed;return result_Bed;}int SonarRead_Water(){int result_Water;char inData_Water[5]; //char array to read data intoint index_water = 0;sonarSerial_Water.listen();while (sonarSerial_Water.read() != -1) {}while (stringComplete_Water == false) {if (sonarSerial_Water.available()){char rByte = sonarSerial_Water.read(); //read serial input for "R" to mark start of dataif(rByte == 'R'){//Serial.println("rByte set");while (index_water < 4) //read next three character for range from sensor{if (sonarSerial_Water.available()){inData_Water[index_water] = sonarSerial_Water.read();//Serial.println(inData_Water[index_water]); //Debug lineindex_water++; // Increment where to write next}}inData_Water[index_water] = 0x00; //add a padding byte at end for atoi() function}rByte = 0; //reset the rByte ready for next readingindex_water = 0; // Reset index ready for next readingstringComplete_Water = true; // Set completion of read to trueresult_Water = atoi(inData_Water); // Changes string data into an integer for use}}dataRec += ",";dataRec += result_Water;return result_Water;} -
2018-08-20 at 7:32 PM #12355
Short answer: you just can’t use on 7 for the Maxbotix using the libraries and sketch as you have it written.
Long answer: It’s a pin change interrupt conflict problem. The software serial library version you’re using is a modified version that only has access to vectors 1 and 2. Pin 7 is on vector 3. The ReadMe for ModularSensors has more details about how to deal with this type of conflict in serial input libraries. I might be able to give more details later if you need it, but that’s the ghist of it.
-
2018-08-21 at 3:06 PM #12357
Thanks for the response. I have looked over the ReadMe but am very new to this all. Do you have any recommendations for which serial input library to use or how to modify to allow two grove connections?
Alternatively, I could continue using 10/11 and 18/19 – is there a suitable and easy way to securely attach (for remote deployment) to 18/19 you could suggest?
-
2018-08-21 at 9:28 PM #12358
How (physically) are you connecting to the Maxbotix? Are you using a grove-to-male connector into screw terminals on the sensors? Are the sensors being positioned (in the field) in a way that their sonar beams could interfere with each other?
If you want to use the libraries you have and leave your code as is, you could twist your cables up such that the two serial inputs are 10 and 11 and the two excites are any two other pins together on a grove (4/5, 6/7, etc). Just setting the pin mode or pin level high/low does not interfere with the pin change interrupts. In that case bed might be 10 data/6 excite and water 11 data/7 excite. There’s certainly no rule that the pins have to be next to each other.
If the sonars are positioned in the world such that the won’t interfere with each other, you could also completely disconnect the excite pin and just let them free range with the data going to pins 10 and 11. You would need to make sure you clear your serial buffer before each reading, though, or your buffer will fill up and you’ll only be reading out older data instead of the newest point.
Since it looks like you’re also using the PCInt library that’s cropped to just vector 0, if you really want to use a single grove for each Maxbotix, you could instead differently modify SoftwareSerial so that it can control 1, 2, and 3 instead of just 1 and 2. That would allow you to use any pin from 0-23 on software serial. You cannot use the unmodified PCInt and SoftwareSerial libraries together. Or you can drop both the Sodaq PCInt and SoftwareSerial and switch to GreyGnomes EnableInterrupt as your pin change master and then use NeoSWSerial with the interrupts handled by EnableInterrupt instead of by NeoSWSerial directly. (You have to un-comment the appropriate line in the NeoSWSerial library to allow that.) But if what I’m saying doesn’t make much sense, or you don’t feel confident modifying libraries, I’d suggest you go with twisting the wires.
Some other notes on your code:
Using the timer library is unnecessary. You’re setting the board to go to sleep and then wake up when there’s an interrupt. Then you’re setting the clock to create that interrupt every minute. When the clock wakes the board up on the even minute, it will immediately start at the beginning of your loop. It will run through the loop until it gets to the sleep function and then will go to sleep until the next interrupt. You also do a check just in case to make sure that no other interrupt (except the timer minute) sets off any of the interesting code in the loop. So, if you want to rewrite, ditch the timer. Having the timer shouldn’t hurt anything, though.
Also, that code looking for the “R” and then reading the next four bytes looks like code that was at one point on the Maxbotix site, but I think using the “parseInt” stream function is simpler and works better.
-
2018-08-23 at 11:48 AM #12360
Thank you very much for the suggestions. I’ve decided to go with 10/6 for one sensor and 11/7 for the other – a very easy solution! I’ve also switched to using the parseInt stream function. Thanks again.
-
-
AuthorPosts
- You must be logged in to reply to this topic.