| /* |
| * Copyright (c) 2017 FH Dortmund and others |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Description: |
| * Rover Sensors API - Interfaces for Rover sensors application development |
| * |
| * Contributors: |
| * M.Ozcelikors <mozcelikors@gmail.com>, created C++ API 17.11.2017 |
| * initial QMC5883L driver implemented 30.11.2017 |
| * David Schmelter, Fraunhofer IEM - compass sensor initial implementation |
| * Gael Blondelle - initial API and parameters |
| * |
| */ |
| |
| #include <roverapi/basic_psys_rover.h> |
| #include <roverapi/rover_sensors.hpp> |
| #include <mcp3004.h> |
| #include <wiringPiI2C.h> |
| #include <math.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| |
| static int i2c_hmc588l_fd = -1; |
| |
| static int16_t xMinRaw = 0; |
| static int16_t xMaxRaw = 0; |
| static int16_t yMaxRaw = 0; |
| static int16_t yMinRaw = 0; |
| |
| rover::RoverSensors::RoverSensors() |
| { |
| this->calibration_start = 0; |
| } |
| |
| void rover::RoverSensors::initialize (void) |
| { |
| //wiringPiSetup(); |
| |
| this->setupHCSR04UltrasonicSensor(this->ROVER_FRONT); |
| this->setupHCSR04UltrasonicSensor(this->ROVER_REAR); |
| this->setupInfraredSensors(); |
| this->setupBearingHMC5883L(); |
| this->setupBearingQMC5883L(); |
| } |
| |
| void rover::RoverSensors::setupHCSR04UltrasonicSensor (int sensor_id) |
| { |
| int trig_pin, echo_pin; |
| |
| if (sensor_id == this->ROVER_FRONT) |
| { |
| trig_pin = this->TRIG1; |
| echo_pin = this->ECHO1; |
| } |
| else if (sensor_id == this->ROVER_REAR) |
| { |
| trig_pin = this->TRIG0; |
| echo_pin = this->ECHO0; |
| } |
| else |
| { |
| printf ("Invalid Sensor ID for the Ultrasonic Sensor to function setupHCSR04UltrasonicSensor.\n"); |
| } |
| |
| pinMode(trig_pin, OUTPUT); |
| pinMode(echo_pin, INPUT); |
| |
| //TRIG pin must start LOW |
| digitalWrite(trig_pin, LOW); |
| delayMicroseconds(2); |
| } |
| |
| int rover::RoverSensors::readHCSR04UltrasonicSensor (int sensor_id) |
| { |
| int trig_pin, echo_pin; |
| |
| if (sensor_id == this->ROVER_FRONT) |
| { |
| trig_pin = this->TRIG1; |
| echo_pin = this->ECHO1; |
| } |
| else if (sensor_id == this->ROVER_REAR) |
| { |
| trig_pin = this->TRIG0; |
| echo_pin = this->ECHO0; |
| } |
| else |
| { |
| printf ("Invalid Sensor ID for the Ultrasonic Sensor to function readHCSR04UltrasonicSensor.\n"); |
| } |
| |
| int distance = 0; |
| //Send trig pulse |
| digitalWrite(trig_pin, HIGH); |
| delayMicroseconds(10); |
| digitalWrite(trig_pin, LOW); |
| |
| //Wait for echo start |
| long startTime = micros(); |
| while(digitalRead(echo_pin) == LOW && micros() < startTime + 100000); |
| |
| //Wait for echo end |
| startTime = micros(); |
| while(digitalRead(echo_pin) == HIGH); |
| long travelTime = micros() - startTime; |
| |
| //Get distance in cm |
| distance = travelTime * 34300; |
| distance = distance / 1000000; |
| distance = distance / 2; |
| // The below protection is to ensure there is no value fluctuation due to timeout |
| if (distance > 40 ) |
| distance = 40; |
| |
| // printf("dist=%d\n",distance); |
| return distance; |
| } |
| |
| void rover::RoverSensors::setupGrooveUltrasonicSensor(void) { |
| //wiringPiSetup(); //Since this can only be used once in a program, we do it in main and comment this. |
| } |
| |
| int rover::RoverSensors::readGrooveUltrasonicSensor (int sensor_id) |
| { |
| int sig_pin; |
| |
| if (sensor_id == this->ROVER_FRONT) |
| { |
| sig_pin = this->SIG1; |
| } |
| else if (sensor_id == this->ROVER_REAR) |
| { |
| sig_pin = this->SIG0; |
| } |
| else |
| { |
| printf ("Invalid Sensor ID for the Ultrasonic Sensor to function readGrooveUltrasonicSensor.\n"); |
| } |
| |
| long startTime, stopTime, elapsedTime, distance = 0; |
| pinMode(sig_pin, OUTPUT); |
| digitalWrite(sig_pin, LOW); |
| delayMicroseconds(2); |
| digitalWrite(sig_pin, HIGH); |
| delayMicroseconds(5); |
| digitalWrite(sig_pin, LOW); |
| pinMode(sig_pin,INPUT); |
| |
| startTime = micros(); |
| while (digitalRead(sig_pin) == LOW ); |
| startTime = micros(); |
| // For values above 40cm, groove sensor is unable to receive signals which causes it to stuck |
| // This is resolved by adding the timeout below. |
| // However, this timeout cause values bigger than 40 to fluctuate |
| while (digitalRead(sig_pin) == HIGH && micros() < startTime + 100000); |
| stopTime = micros(); |
| elapsedTime = stopTime - startTime; |
| distance = elapsedTime / 29 /2; |
| // The below protection is to ensure there is no value fluctuation |
| if (distance > 40 ) |
| distance = 40; |
| return distance; |
| } |
| |
| void rover::RoverSensors::setupInfraredSensors (void) |
| { |
| /* Init the analog digital converter */ |
| mcp3004Setup (BASE, SPI_CHAN); // 3004 and 3008 are the same 4/8 channels |
| } |
| |
| float rover::RoverSensors::readInfraredSensor (int infrared_sensor_id) |
| { |
| float x; |
| float y = analogRead (BASE+infrared_sensor_id); |
| |
| if (infrared_sensor_id != this->ROVER_FRONT_LEFT && |
| infrared_sensor_id != this->ROVER_FRONT_RIGHT && |
| infrared_sensor_id != this->ROVER_REAR_LEFT && |
| infrared_sensor_id != this->ROVER_REAR_RIGHT) |
| { |
| printf ("Invalid Sensor ID for the Infrared Sensor to function readInfraredSensor.\n"); |
| } |
| |
| // 1/cm to output voltage is almost linear between |
| // 80cm->0,4V->123 |
| // 6cm->3,1V->961 |
| // => y=5477*x+55 => x= (y-55)/5477 |
| if (y<123){ |
| x=100.00; |
| } else { |
| float inverse = (y-55)/5477; |
| //printf("inverse=%f\n",inverse); |
| // x is the distance in cm |
| x = 1/inverse; |
| } |
| |
| return x; |
| } |
| |
| void rover::RoverSensors::calibrateBearingSensor (void) |
| { |
| this->calibration_start = millis(); |
| } |
| |
| void rover::RoverSensors::setupBearingQMC5883L(void) |
| { |
| this->setHMC588LAddress(0x0D); |
| this->setHMC588LCalibrationPeriod(10000); |
| this->setHMC588LDeclinationAngle(0.0413); |
| |
| if ((i2c_hmc588l_fd = wiringPiI2CSetup(this->HMC588L_ADDRESS)) < 0) |
| { |
| printf("Failed to initialize HMC588L compass sensor"); |
| } |
| |
| if (i2c_hmc588l_fd >= 0) |
| { |
| wiringPiI2CWriteReg8 (i2c_hmc588l_fd, 0x0B, 0x01); //init SET/PERIOD register |
| |
| /* |
| Define |
| OSR = 512 |
| Full Scale Range = 8G(Gauss) |
| ODR = 200HZ |
| set continuous measurement mode |
| */ |
| wiringPiI2CWriteReg8 (i2c_hmc588l_fd, 0x09, 0x1D); |
| } |
| |
| this->calibration_start = millis(); |
| |
| // |
| // To do a software reset |
| // wiringPiI2CWriteReg8 (i2c_hmc588l_fd, 0x0A, 0x80); |
| // |
| |
| } |
| |
| float rover::RoverSensors::readBearingQMC5883L(void) |
| { |
| int8_t buffer[6]; |
| |
| //wiringPiI2CWrite (i2c_hmc588l_fd, 0x00); //Start with register 3 |
| //delay(25); |
| |
| buffer[0] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x00); //LSB x |
| buffer[1] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x01); //MSB x |
| buffer[2] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x02); //LSB y |
| buffer[3] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x03); //MSB y |
| buffer[4] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x04); //LSB z |
| buffer[5] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x05); //MSB z |
| |
| int16_t xRaw = (((int16_t) buffer[1] << 8) & 0xff00) | buffer[0]; |
| int16_t yRaw = (((int16_t) buffer[3] << 8) & 0xff00) | buffer[2]; |
| #ifdef DEBUG |
| printf ("%d %d\n",xRaw,yRaw); |
| #endif |
| |
| //if calibration is active calculate minimum and maximum x/y values for calibration |
| if (millis() <= this->calibration_start + this->CALIBRATION_DURATION) { |
| if (xRaw < xMinRaw) { |
| xMinRaw = xRaw; |
| } |
| if (xRaw > xMaxRaw) { |
| xMaxRaw = xRaw; |
| } |
| if (yRaw < yMinRaw) { |
| yMinRaw = yRaw; |
| } |
| if (yRaw > yMaxRaw) { |
| yMaxRaw = yRaw; |
| } |
| } |
| |
| //calibration: move and scale x coordinates based on minimum and maximum values to get a unit circle |
| float xf = xRaw - (float) (xMinRaw + xMaxRaw) / 2.0f; |
| xf = xf / (xMinRaw + xMaxRaw) * 2.0f; |
| |
| //calibration: move and scale y coordinates based on minimum and maximum values to get a unit circle |
| float yf = yRaw - (float) (yMinRaw + yMaxRaw) / 2.0f; |
| yf = yf / (yMinRaw + yMaxRaw) * 2.0f; |
| |
| float bearing = atan2(yf, xf); |
| #ifdef DEBUG |
| printf("%f, bearing\n", bearing); |
| #endif |
| |
| //location specific magnetic field correction |
| bearing += this->DECLINATION_ANGLE; |
| |
| if (bearing < 0) { |
| bearing += 2 * M_PI; |
| } |
| |
| if (bearing > 2 * M_PI) { |
| bearing -= 2 * M_PI; |
| } |
| |
| float headingDegrees = bearing * (180.0 / M_PI); |
| #ifdef DEBUG |
| printf("%lf, headingDegrees\n", headingDegrees); |
| #endif |
| return headingDegrees; |
| |
| |
| } |
| |
| void rover::RoverSensors::setupBearingHMC5883L(void) |
| { |
| this->setHMC588LAddress(0x1E); |
| this->setHMC588LCalibrationPeriod(10000); |
| this->setHMC588LDeclinationAngle(0.0413); |
| |
| #ifdef DEBUG |
| printf ("HMC588L Address is: %x\n", this->HMC588L_ADDRESS); |
| printf ("HMC588L Calibration period is %d\n", this->CALIBRATION_DURATION); |
| printf ("HMC588L Declination angle is %f\n", this->DECLINATION_ANGLE); |
| #endif |
| |
| if ((i2c_hmc588l_fd = wiringPiI2CSetup(this->HMC588L_ADDRESS)) < 0) { |
| printf("Failed to initialize HMC588L compass sensor"); |
| } |
| |
| if (i2c_hmc588l_fd >= 0) { |
| int8_t gain = 5; |
| |
| wiringPiI2CWriteReg8(i2c_hmc588l_fd, 0x00, 0x70); // 8-average, 15 Hz default, positive self test measurement |
| wiringPiI2CWriteReg8(i2c_hmc588l_fd, 0x01, gain << 5); // Gain |
| wiringPiI2CWriteReg8(i2c_hmc588l_fd, 0x02, 0x00); // Continuous-measurement mode |
| } |
| |
| this->calibration_start = millis(); |
| } |
| |
| float rover::RoverSensors::readBearingHMC5883L(void) |
| { |
| int8_t buffer[6]; |
| |
| //potential optimization: wiringPiI2CReadReg16 |
| buffer[0] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x03); //MSB x |
| buffer[1] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x04); //LSB x |
| buffer[2] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x05); //MSB z |
| buffer[3] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x06); //LSB z |
| buffer[4] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x07); //MSB y |
| buffer[5] = wiringPiI2CReadReg8(i2c_hmc588l_fd, 0x08); //LSB y |
| |
| int16_t xRaw = (((int16_t) buffer[0] << 8) & 0xff00) | buffer[1]; |
| //int16_t zRaw = (((int16_t) buffer[2] << 8) & 0xff00) | buffer[3]; |
| int16_t yRaw = (((int16_t) buffer[4] << 8) & 0xff00) | buffer[5]; |
| |
| //if calibration is active calculate minimum and maximum x/y values for calibration |
| if (millis() <= this->calibration_start + this->CALIBRATION_DURATION) { |
| if (xRaw < xMinRaw) { |
| xMinRaw = xRaw; |
| } |
| if (xRaw > xMaxRaw) { |
| xMaxRaw = xRaw; |
| } |
| if (yRaw < yMinRaw) { |
| yMinRaw = yRaw; |
| } |
| if (yRaw > yMaxRaw) { |
| yMaxRaw = yRaw; |
| } |
| } |
| |
| //calibration: move and scale x coordinates based on minimum and maximum values to get a unit circle |
| float xf = xRaw - (float) (xMinRaw + xMaxRaw) / 2.0f; |
| xf = xf / (xMinRaw + xMaxRaw) * 2.0f; |
| |
| //calibration: move and scale y coordinates based on minimum and maximum values to get a unit circle |
| float yf = yRaw - (float) (yMinRaw + yMaxRaw) / 2.0f; |
| yf = yf / (yMinRaw + yMaxRaw) * 2.0f; |
| |
| float bearing = atan2(yf, xf); |
| #ifdef DEBUG |
| printf("%f, bearing\n", bearing); |
| #endif |
| |
| //location specific magnetic field correction |
| bearing += this->DECLINATION_ANGLE; |
| |
| if (bearing < 0) { |
| bearing += 2 * M_PI; |
| } |
| |
| if (bearing > 2 * M_PI) { |
| bearing -= 2 * M_PI; |
| } |
| |
| float headingDegrees = bearing * (180.0 / M_PI); |
| #ifdef DEBUG |
| printf("%lf, headingDegrees\n", headingDegrees); |
| #endif |
| return headingDegrees; |
| } |
| |
| float rover::RoverSensors::readTemperature (void) |
| { |
| int data[5] = { 0, 0, 0, 0, 0 }; |
| |
| uint8_t laststate; |
| uint8_t counter; |
| uint8_t j; |
| uint8_t i; |
| |
| int try_again = 1; |
| float f, h, c; |
| |
| while (try_again == 1) |
| { |
| data[0] = data[1] = data[2] = data[3] = data[4] = 0; |
| laststate = HIGH; |
| counter = 0; |
| j = 0; |
| |
| /* pull pin down for 18 milliseconds */ |
| pinMode( this->DHT22_RPI_PIN, OUTPUT ); |
| digitalWrite( this->DHT22_RPI_PIN, LOW ); |
| delay( 18 ); |
| |
| /* prepare to read the pin */ |
| pinMode( this->DHT22_RPI_PIN, INPUT ); |
| |
| /* detect change and read data */ |
| for ( i = 0; i < this->MAX_TIMINGS; i++ ) |
| { |
| counter = 0; |
| while ( digitalRead( this->DHT22_RPI_PIN ) == laststate ) |
| { |
| counter++; |
| delayMicroseconds( 1 ); |
| if ( counter == 255 ) |
| { |
| break; |
| } |
| } |
| laststate = digitalRead( this->DHT22_RPI_PIN ); |
| |
| if ( counter == 255 ) |
| break; |
| |
| /* ignore first 3 transitions */ |
| if ( (i >= 4) && (i % 2 == 0) ) |
| { |
| /* shove each bit into the storage bytes */ |
| data[j / 8] <<= 1; |
| if ( counter > 16 ) |
| data[j / 8] |= 1; |
| j++; |
| } |
| } |
| |
| /* |
| * check we read 40 bits (8bit x 5 ) + verify checksum in the last byte |
| * print it out if data is good |
| */ |
| if ( (j >= 40) && |
| (data[4] == ( (data[0] + data[1] + data[2] + data[3]) & 0xFF) ) ) |
| { |
| h = (float)((data[0] << 8) + data[1]) / 10; |
| if ( h > 100 ) |
| { |
| h = data[0]; // for DHT11 |
| } |
| c = (float)(((data[2] & 0x7F) << 8) + data[3]) / 10; |
| if ( c > 125 ) |
| { |
| c = data[2]; // for DHT11 |
| } |
| if ( data[2] & 0x80 ) |
| { |
| c = -c; |
| } |
| f = c * 1.8f + 32; |
| #ifdef DEBUG |
| printf( "Humidity = %.1f %% Temperature = %.1f *C (%.1f *F)\n", h, c, f ); |
| #endif |
| try_again = 0; |
| } |
| else |
| { |
| /* Data not good */ |
| try_again = 1; |
| //printf ("Data not good, skipping\n"); |
| |
| } |
| } |
| |
| /* Return temperature */ |
| return c; |
| } |
| |
| float rover::RoverSensors::readHumidity (void) |
| { |
| int data[5] = { 0, 0, 0, 0, 0 }; |
| |
| uint8_t laststate; |
| uint8_t counter; |
| uint8_t j; |
| uint8_t i; |
| |
| int try_again = 1; |
| float f, h, c; |
| |
| while (try_again == 1) |
| { |
| data[0] = data[1] = data[2] = data[3] = data[4] = 0; |
| laststate = HIGH; |
| counter = 0; |
| j = 0; |
| |
| /* pull pin down for 18 milliseconds */ |
| pinMode( this->DHT22_RPI_PIN, OUTPUT ); |
| digitalWrite( this->DHT22_RPI_PIN, LOW ); |
| delay( 18 ); |
| |
| /* prepare to read the pin */ |
| pinMode( this->DHT22_RPI_PIN, INPUT ); |
| |
| /* detect change and read data */ |
| for ( i = 0; i < this->MAX_TIMINGS; i++ ) |
| { |
| counter = 0; |
| while ( digitalRead( this->DHT22_RPI_PIN ) == laststate ) |
| { |
| counter++; |
| delayMicroseconds( 1 ); |
| if ( counter == 255 ) |
| { |
| break; |
| } |
| } |
| laststate = digitalRead( this->DHT22_RPI_PIN ); |
| |
| if ( counter == 255 ) |
| break; |
| |
| /* ignore first 3 transitions */ |
| if ( (i >= 4) && (i % 2 == 0) ) |
| { |
| /* shove each bit into the storage bytes */ |
| data[j / 8] <<= 1; |
| if ( counter > 16 ) |
| data[j / 8] |= 1; |
| j++; |
| } |
| } |
| |
| /* |
| * check we read 40 bits (8bit x 5 ) + verify checksum in the last byte |
| * print it out if data is good |
| */ |
| if ( (j >= 40) && |
| (data[4] == ( (data[0] + data[1] + data[2] + data[3]) & 0xFF) ) ) |
| { |
| h = (float)((data[0] << 8) + data[1]) / 10; |
| if ( h > 100 ) |
| { |
| h = data[0]; // for DHT11 |
| } |
| c = (float)(((data[2] & 0x7F) << 8) + data[3]) / 10; |
| if ( c > 125 ) |
| { |
| c = data[2]; // for DHT11 |
| } |
| if ( data[2] & 0x80 ) |
| { |
| c = -c; |
| } |
| f = c * 1.8f + 32; |
| #ifdef DEBUG |
| printf( "Humidity = %.1f %% Temperature = %.1f *C (%.1f *F)\n", h, c, f ); |
| #endif |
| try_again = 0; |
| } |
| else |
| { |
| /* Data not good */ |
| try_again = 1; |
| //printf ("Data not good, skipping\n"); |
| } |
| } |
| |
| /* Return humidity */ |
| return h; |
| } |
| |
| void rover::RoverSensors::setHMC588LAddress (int address) |
| { |
| this->HMC588L_ADDRESS = address; |
| } |
| |
| void rover::RoverSensors::setHMC588LDeclinationAngle (float angle) |
| { |
| this->DECLINATION_ANGLE = angle; |
| } |
| |
| void rover::RoverSensors::setHMC588LCalibrationPeriod(int period) |
| { |
| this->CALIBRATION_DURATION = period; |
| } |