first commit
This commit is contained in:
commit
a5a0434432
1126 changed files with 439481 additions and 0 deletions
416
Software/Cpp/grovepi.cpp
Normal file
416
Software/Cpp/grovepi.cpp
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
// GrovePi C++ library
|
||||
// v0.2
|
||||
//
|
||||
// This library provides the basic functions for using the GrovePi in C
|
||||
//
|
||||
// The GrovePi connects the Raspberry Pi and Grove sensors. You can learn more about GrovePi here: http://www.dexterindustries.com/GrovePi
|
||||
//
|
||||
// Have a question about this example? Ask on the forums here: http://forum.dexterindustries.com/c/grovepi
|
||||
//
|
||||
// History
|
||||
// ------------------------------------------------
|
||||
// Author Date Comments
|
||||
// Karan 28 Dec 2015 Initial Authoring
|
||||
// Robert April 2017 Continuing
|
||||
|
||||
/*
|
||||
License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
GrovePi for the Raspberry Pi: an open source platform for connecting Grove Sensors to the Raspberry Pi.
|
||||
Copyright (C) 2017 Dexter Industries
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "grovepi.h"
|
||||
|
||||
static const bool DEBUG = false;
|
||||
static const int RBUFFER_SIZE = 32;
|
||||
static const int WBUFFER_SIZE = 4;
|
||||
|
||||
static int file_device = 0;
|
||||
static int max_i2c_retries = 5;
|
||||
|
||||
static const uint8_t DIGITAL_READ = 1;
|
||||
static const uint8_t DIGITAL_WRITE = 2;
|
||||
static const uint8_t ANALOG_READ = 3;
|
||||
static const uint8_t ANALOG_WRITE = 4;
|
||||
static const uint8_t PIN_MODE = 5;
|
||||
static const uint8_t USONIC_READ = 7;
|
||||
|
||||
namespace GrovePi
|
||||
{
|
||||
|
||||
// variables which can be used
|
||||
// in the user-program
|
||||
const uint8_t INPUT = 0;
|
||||
const uint8_t OUTPUT = 1;
|
||||
const bool LOW = false;
|
||||
const bool HIGH = true;
|
||||
uint8_t GROVE_ADDRESS = 0x04;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* determines the revision of the raspberry hardware
|
||||
* @return revision number
|
||||
*/
|
||||
static uint8_t gpioHardwareRevision()
|
||||
{
|
||||
int revision = 0;
|
||||
FILE * filp = fopen("/proc/cpuinfo", "r");
|
||||
char buffer[512];
|
||||
char term;
|
||||
|
||||
if(filp != NULL)
|
||||
{
|
||||
while(fgets(buffer,sizeof(buffer),filp) != NULL)
|
||||
{
|
||||
if(!strncasecmp("revision\t", buffer, 9))
|
||||
{
|
||||
if(sscanf(buffer + strlen(buffer) - 5, "%x%c", &revision, &term) == 2)
|
||||
{
|
||||
if(term == '\n')
|
||||
break;
|
||||
revision = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(filp);
|
||||
}
|
||||
return revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* determines wheter I2C is found at "/dev/i2c-0" or "/dev/i2c-1"
|
||||
* depending on the raspberry model
|
||||
*
|
||||
* @param smbus_name string to hold the filename
|
||||
*
|
||||
* hw_rev 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
* Type.1 X X - - X - - X X X X X - - X X
|
||||
* Type.2 - - X X X - - X X X X X - - X X
|
||||
* Type.3 X X X X X X X X X X X X X X
|
||||
*
|
||||
* hw_rev 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
||||
* Type.1 - X X - - X X X X X - - - - - -
|
||||
* Type.2 - X X - - - X X X X - X X X X X
|
||||
* Type.3 X X X X X X X X X X X X - - - -
|
||||
*
|
||||
*/
|
||||
void GrovePi::SMBusName(char *smbus_name)
|
||||
{
|
||||
unsigned int hw_revision = gpioHardwareRevision();
|
||||
unsigned int smbus_rev;
|
||||
|
||||
if(hw_revision < 4)
|
||||
// type 1
|
||||
smbus_rev = 1;
|
||||
else if(hw_revision < 16)
|
||||
// type 2
|
||||
smbus_rev = 2;
|
||||
else
|
||||
// type 3
|
||||
smbus_rev = 3;
|
||||
|
||||
if(smbus_rev == 2 || smbus_rev == 3)
|
||||
strcpy(smbus_name, "/dev/i2c-1");
|
||||
else
|
||||
strcpy(smbus_name, "/dev/i2c-0");
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the threshold limit
|
||||
* at which an exception is thrown
|
||||
* @param _max_i2c_retries number of retries
|
||||
*/
|
||||
void GrovePi::setMaxI2CRetries(int _max_i2c_retries)
|
||||
{
|
||||
// each failed retry takes 1 second
|
||||
// so the number of [max_i2c_retries]
|
||||
// represents the timeout on a
|
||||
// transaction / connection in seconds
|
||||
max_i2c_retries = _max_i2c_retries;
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to get communication w/ the GrovePi
|
||||
* throws I2CError on failing to establish communication
|
||||
* @param 7-bit address of the slave device
|
||||
*/
|
||||
void GrovePi::initGrovePi()
|
||||
{
|
||||
file_device = initDevice(GROVE_ADDRESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* for setting the address of an I2C device
|
||||
* @param address 7-bit address of the slave device
|
||||
*/
|
||||
int GrovePi::initDevice(uint8_t address)
|
||||
{
|
||||
char filename[11]; // enough to hold "/dev/i2c-x"
|
||||
int current_retry = 0;
|
||||
int i2c_file_device;
|
||||
SMBusName(filename);
|
||||
|
||||
// try to connect for a number of times
|
||||
while(current_retry < max_i2c_retries)
|
||||
{
|
||||
// increase the counter
|
||||
current_retry += 1;
|
||||
|
||||
// open port for read/write operation
|
||||
if((i2c_file_device = open(filename, O_RDWR)) < 0)
|
||||
{
|
||||
printf("[failed to open i2c port]\n");
|
||||
// try in the next loop to connect
|
||||
continue;
|
||||
}
|
||||
// setting up port options and address of the device
|
||||
if(ioctl(i2c_file_device, I2C_SLAVE, address) < 0)
|
||||
{
|
||||
printf("[unable to get bus access to talk to slave]\n");
|
||||
// try in the next loop to connect
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it got connected, then exit
|
||||
break;
|
||||
}
|
||||
|
||||
// if connection couldn't be established
|
||||
// throw exception
|
||||
if(current_retry == max_i2c_retries)
|
||||
throw I2CError("[I2CError on opening port]\n");
|
||||
|
||||
return i2c_file_device;
|
||||
}
|
||||
|
||||
void GrovePi::setGrovePiAddress(uint8_t address)
|
||||
{
|
||||
GROVE_ADDRESS = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes a block of [WBUFFER_SIZE] bytes to the slave i2c device
|
||||
* throws I2CError on failing to send data
|
||||
* @param command command to send to GrovePi
|
||||
* @param pin_number number
|
||||
* @param opt1 optional argument depending on sensor/actuator/etc
|
||||
* @param opt2 optional argument depending on sensor/actuator/etc
|
||||
*/
|
||||
void GrovePi::writeBlock(uint8_t command, uint8_t pin_number, uint8_t opt1, uint8_t opt2)
|
||||
{
|
||||
int output_code = -1;
|
||||
int current_retry = 0;
|
||||
uint8_t data_block[WBUFFER_SIZE] = {command, pin_number, opt1, opt2};
|
||||
|
||||
// repeat until it writes the data
|
||||
// or until it fails sending it
|
||||
while(output_code == -1 && current_retry < max_i2c_retries)
|
||||
{
|
||||
output_code = i2c_smbus_write_i2c_block_data(file_device, 1, WBUFFER_SIZE, &data_block[0]);
|
||||
current_retry += 1;
|
||||
}
|
||||
|
||||
// if the error persisted
|
||||
// after retrying for [max_i2c_retries] retries
|
||||
// then throw exception
|
||||
if(output_code == -1)
|
||||
throw I2CError("[I2CError writing block: max retries reached]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* sends a single byte to the i2c slave
|
||||
* throws I2CError on failing to send data
|
||||
* @param byte_val byte to be sent
|
||||
*/
|
||||
void GrovePi::writeByte(uint8_t byte_val)
|
||||
{
|
||||
uint8_t data_block[WBUFFER_SIZE] = {byte_val};
|
||||
uint8_t length = 1;
|
||||
int current_retry = 0;
|
||||
int output_code = 0;
|
||||
|
||||
// repeat until it writes the data
|
||||
// or until it fails sending it
|
||||
while(output_code != length && current_retry < max_i2c_retries)
|
||||
{
|
||||
output_code = write(file_device, data_block, length);
|
||||
current_retry += 1;
|
||||
}
|
||||
|
||||
// if the error persisted
|
||||
// after retrying for [max_i2c_retries] retries
|
||||
// then throw exception
|
||||
if(output_code != length)
|
||||
throw I2CError("[I2CError writing byte: max retries reached]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* reads a block of [RBUFFER_SIZE] bytes from the slave device
|
||||
* throws I2CError on failing to read data
|
||||
* @param data_block pointer to hold the read data
|
||||
* @return number of bytes read
|
||||
*/
|
||||
uint8_t GrovePi::readBlock(uint8_t *data_block)
|
||||
{
|
||||
int current_retry = 0;
|
||||
int output_code = 0;
|
||||
|
||||
// repeat until it reads the data
|
||||
// or until it fails sending it
|
||||
while(output_code == 0 && current_retry < max_i2c_retries)
|
||||
{
|
||||
output_code = i2c_smbus_read_i2c_block_data(file_device, 1, RBUFFER_SIZE, data_block);
|
||||
current_retry += 1;
|
||||
}
|
||||
|
||||
// if the error persisted
|
||||
// after retrying for [max_i2c_retries] retries
|
||||
// then throw exception
|
||||
if(output_code == 0)
|
||||
throw I2CError("[I2CError reading block: max retries reached]\n");
|
||||
|
||||
return output_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads 1 byte from the slave device
|
||||
* throws I2CError on failing to read data
|
||||
* @return value read from the slave device
|
||||
*/
|
||||
uint8_t GrovePi::readByte()
|
||||
{
|
||||
int current_retry = 0;
|
||||
int output_code = -1;
|
||||
|
||||
// repeat until it reads the data
|
||||
// or until it fails sending it
|
||||
while((output_code < 0 || output_code == 255) && current_retry < max_i2c_retries)
|
||||
{
|
||||
output_code = i2c_smbus_read_byte(file_device);
|
||||
current_retry += 1;
|
||||
}
|
||||
|
||||
// if the error persisted
|
||||
// after retrying for [max_i2c_retries] retries
|
||||
// then throw exception
|
||||
if(output_code < 0 || output_code == 255)
|
||||
throw I2CError("[I2CError reading byte: max retries reached]\n");
|
||||
|
||||
return output_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* sleep raspberry
|
||||
* @param milliseconds time
|
||||
*/
|
||||
void GrovePi::delay(unsigned int milliseconds)
|
||||
{
|
||||
usleep(milliseconds * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* set pin as OUTPUT or INPUT
|
||||
* @param pin number
|
||||
* @param mode OUTPUT/INPUT
|
||||
*/
|
||||
void GrovePi::pinMode(uint8_t pin, uint8_t mode)
|
||||
{
|
||||
writeBlock(PIN_MODE, pin, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* set a pin as HIGH or LOW
|
||||
* @param pin number
|
||||
* @param value HIGH or LOW
|
||||
*/
|
||||
void GrovePi::digitalWrite(uint8_t pin, bool value)
|
||||
{
|
||||
writeBlock(DIGITAL_WRITE, pin, (uint8_t)value);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads whether a pin is HIGH or LOW
|
||||
* @param pin number
|
||||
* @return HIGH or LOW
|
||||
*/
|
||||
bool GrovePi::digitalRead(uint8_t pin)
|
||||
{
|
||||
writeBlock(DIGITAL_READ, pin);
|
||||
return readByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* describe at a desired pin a voltage between 0 and VCC
|
||||
* @param pin number
|
||||
* @param value 0-255
|
||||
*/
|
||||
void GrovePi::analogWrite(uint8_t pin, uint8_t value)
|
||||
{
|
||||
writeBlock(ANALOG_WRITE, pin, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads analog data from grovepi sensor(s)
|
||||
* @param pin number
|
||||
* @return 16-bit data
|
||||
*/
|
||||
short GrovePi::analogRead(uint8_t pin)
|
||||
{
|
||||
uint8_t data[32];
|
||||
writeBlock(ANALOG_READ, pin);
|
||||
readBlock(data);
|
||||
|
||||
short output = (data[1] << 8) + data[2];
|
||||
if(output == 65535)
|
||||
output = -1;
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* to be completed
|
||||
* @param pin number
|
||||
* @return time taken for the sound to travel back?
|
||||
*/
|
||||
short GrovePi::ultrasonicRead(uint8_t pin)
|
||||
{
|
||||
uint8_t incoming[32];
|
||||
short output;
|
||||
writeBlock(USONIC_READ, pin);
|
||||
delay(60);
|
||||
|
||||
readByte();
|
||||
readBlock(incoming);
|
||||
|
||||
output = (incoming[1] << 8) + incoming[2];
|
||||
if(output == (2 << 16) - 1)
|
||||
output = -1;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
const char* GrovePi::I2CError::detail()
|
||||
{
|
||||
return this->what();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue