From d1578805fb9d220eb8ff813bd3375608e8dc2805 Mon Sep 17 00:00:00 2001 From: Philippe Roy Date: Thu, 11 Jan 2024 01:12:50 +0100 Subject: [PATCH] =?UTF-8?q?Programme=20Arduino=20sp=C3=A9cifique?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Grove_Motor_Driver_TB6612FNG.cpp | 127 ++ .../Grove_Motor_Driver_TB6612FNG.h | 183 ++ .../porcou-arduino/I2CScanner/I2CScanner.ino | 83 + portail_coulissant/porcou-arduino/I2Cdev.cpp | 1466 +++++++++++++++++ portail_coulissant/porcou-arduino/I2Cdev.h | 274 +++ .../StandardFirmata/LICENSE.txt | 458 +++++ .../StandardFirmata/StandardFirmata.ino | 824 +++++++++ .../porcou-arduino/dc_motor/dc_motor.ino | 37 + .../dc_motor_stop/dc_motor_stop.ino | 21 + .../lib/Grove_Motor_Driver_TB6612FNG.zip | Bin 0 -> 22564 bytes .../porcou-arduino/lib/README.md | 6 + .../porcou-arduino/porcou-arduino.ino | 870 ++++++++++ .../serial/porcou-arduino-v1.ino | 91 + portail_coulissant/porcou_cmd.py | 8 +- 14 files changed, 4444 insertions(+), 4 deletions(-) create mode 100644 portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.cpp create mode 100644 portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.h create mode 100644 portail_coulissant/porcou-arduino/I2CScanner/I2CScanner.ino create mode 100755 portail_coulissant/porcou-arduino/I2Cdev.cpp create mode 100755 portail_coulissant/porcou-arduino/I2Cdev.h create mode 100644 portail_coulissant/porcou-arduino/StandardFirmata/LICENSE.txt create mode 100644 portail_coulissant/porcou-arduino/StandardFirmata/StandardFirmata.ino create mode 100644 portail_coulissant/porcou-arduino/dc_motor/dc_motor.ino create mode 100644 portail_coulissant/porcou-arduino/dc_motor_stop/dc_motor_stop.ino create mode 100644 portail_coulissant/porcou-arduino/lib/Grove_Motor_Driver_TB6612FNG.zip create mode 100644 portail_coulissant/porcou-arduino/lib/README.md create mode 100644 portail_coulissant/porcou-arduino/porcou-arduino.ino create mode 100644 portail_coulissant/porcou-arduino/serial/porcou-arduino-v1.ino diff --git a/portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.cpp b/portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.cpp new file mode 100644 index 0000000..41d061a --- /dev/null +++ b/portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.cpp @@ -0,0 +1,127 @@ +#include "Grove_Motor_Driver_TB6612FNG.h" + +MotorDriver::MotorDriver() { +} + +void MotorDriver::init(uint8_t addr) { + _addr = addr; + standby(); +} + +void MotorDriver::standby() { + I2Cdev::writeByte(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_STANDBY, 0); + delay(1); +} + +void MotorDriver::notStandby() { + I2Cdev::writeByte(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_NOT_STANDBY, 0); + delay(1); +} + +void MotorDriver::setI2cAddr(uint8_t addr) { + if (addr == 0x00) { + return; + } else if (addr >= 0x80) { + return; + } + I2Cdev::writeByte(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_SET_ADDR, addr); + _addr = addr; + delay(100); +} + +void MotorDriver::dcMotorRun(motor_channel_type_t chl, int16_t speed) { + if (speed > 255) { + speed = 255; + } else if (speed < -255) { + speed = -255; + } + + if (speed >= 0) { + _buffer[0] = GROVE_MOTOR_DRIVER_I2C_CMD_CW; + } else { + _buffer[0] = GROVE_MOTOR_DRIVER_I2C_CMD_CCW; + } + + _buffer[1] = chl; + if (speed >= 0) { + _buffer[2] = speed; + } else { + _buffer[2] = (uint8_t)(-speed); + } + + I2Cdev::writeBytes(_addr, _buffer[0], 2, _buffer + 1); + delay(1); +} + +void MotorDriver::dcMotorBrake(motor_channel_type_t chl) { + I2Cdev::writeByte(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_BRAKE, chl); + delay(1); +} + +void MotorDriver::dcMotorStop(motor_channel_type_t chl) { + I2Cdev::writeByte(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_STOP, chl); + delay(1); +} + +void MotorDriver::stepperRun(stepper_mode_type_t mode, int16_t steps, uint16_t rpm) { + uint8_t cw = 0; + // 0.1ms_per_step + uint16_t ms_per_step = 0; + + if (steps > 0) { + cw = 1; + } + // stop + else if (steps == 0) { + stepperStop(); + return; + } else if (steps == -32768) { + steps = 32767; + } else { + steps = -steps; + } + + if (rpm < 1) { + rpm = 1; + } else if (rpm > 300) { + rpm = 300; + } + + ms_per_step = (uint16_t)(3000.0 / (float)rpm); + _buffer[0] = mode; + _buffer[1] = cw; //(cw=1) => cw; (cw=0) => ccw + _buffer[2] = steps; + _buffer[3] = (steps >> 8); + _buffer[4] = ms_per_step; + _buffer[5] = (ms_per_step >> 8); + + I2Cdev::writeBytes(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_RUN, 6, _buffer); + delay(1); +} + +void MotorDriver::stepperStop() { + I2Cdev::writeByte(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_STOP, 0); + delay(1); +} + +void MotorDriver::stepperKeepRun(stepper_mode_type_t mode, uint16_t rpm, bool is_cw) { + // 4=>infinite ccw 5=>infinite cw + uint8_t cw = (is_cw) ? 5 : 4; + // 0.1ms_per_step + uint16_t ms_per_step = 0; + + if (rpm < 1) { + rpm = 1; + } else if (rpm > 300) { + rpm = 300; + } + ms_per_step = (uint16_t)(3000.0 / (float)rpm); + + _buffer[0] = mode; + _buffer[1] = cw; //(cw=1) => cw; (cw=0) => ccw + _buffer[2] = ms_per_step; + _buffer[3] = (ms_per_step >> 8); + + I2Cdev::writeBytes(_addr, GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_KEEP_RUN, 4, _buffer); + delay(1); +} diff --git a/portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.h b/portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.h new file mode 100644 index 0000000..bdc5748 --- /dev/null +++ b/portail_coulissant/porcou-arduino/Grove_Motor_Driver_TB6612FNG.h @@ -0,0 +1,183 @@ +/* + Grove_Motor_Driver_TB6612FNG.h + A library for the Grove - Motor Driver(TB6612FNG) + + Copyright (c) 2018 seeed technology co., ltd. + Website : www.seeed.cc + Author : Jerry Yip + Create Time: 2018-06 + Version : 0.1 + Change Log : + + The MIT License (MIT) + + 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. +*/ + +#ifndef __GROVE_MOTOR_DRIVER_TB6612FNG_H__ +#define __GROVE_MOTOR_DRIVER_TB6612FNG_H__ + +#include "I2Cdev.h" + +#define GROVE_MOTOR_DRIVER_DEFAULT_I2C_ADDR 0x14 + +#define GROVE_MOTOR_DRIVER_DEFAULT_I2C_ADDR 0x14 +#define GROVE_MOTOR_DRIVER_I2C_CMD_BRAKE 0x00 +#define GROVE_MOTOR_DRIVER_I2C_CMD_STOP 0x01 +#define GROVE_MOTOR_DRIVER_I2C_CMD_CW 0x02 +#define GROVE_MOTOR_DRIVER_I2C_CMD_CCW 0x03 +#define GROVE_MOTOR_DRIVER_I2C_CMD_STANDBY 0x04 +#define GROVE_MOTOR_DRIVER_I2C_CMD_NOT_STANDBY 0x05 +#define GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_RUN 0x06 +#define GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_STOP 0x07 +#define GROVE_MOTOR_DRIVER_I2C_CMD_STEPPER_KEEP_RUN 0x08 +#define GROVE_MOTOR_DRIVER_I2C_CMD_SET_ADDR 0x11 + + +enum motor_channel_type_t { + MOTOR_CHA = 0, + MOTOR_CHB = 1, +}; + +enum stepper_mode_type_t { + FULL_STEP = 0, + WAVE_DRIVE = 1, + HALF_STEP = 2, + MICRO_STEPPING = 3, +}; + +class MotorDriver { + public: + MotorDriver(); + + void init(uint8_t addr = GROVE_MOTOR_DRIVER_DEFAULT_I2C_ADDR); + + /************************************************************* + Description + Enter standby mode. Normally you don't need to call this, except that + you have called notStandby() before. + Parameter + Null. + Return + Null. + *************************************************************/ + void standby(); + + /************************************************************* + Description + Exit standby mode. Motor driver does't do any action at this mode. + Parameter + Null. + Return + Null. + *************************************************************/ + void notStandby(); + + /************************************************************* + Description + Set an new I2C address. + Parameter + addr: 0x01~0x7f + Return + Null. + *************************************************************/ + void setI2cAddr(uint8_t addr); + + /************************************************************* + Description + Drive a motor. + Parameter + chl: MOTOR_CHA or MOTOR_CHB + speed: -255~255, if speed > 0, motor moves clockwise. + Note that there is always a starting speed(a starting voltage) for motor. + If the input voltage is 5V, the starting speed should larger than 100 or + smaller than -100. + Return + Null. + *************************************************************/ + void dcMotorRun(motor_channel_type_t chl, int16_t speed); + + /************************************************************* + Description + Brake, stop the motor immediately + Parameter + chl: MOTOR_CHA or MOTOR_CHB + Return + Null. + *************************************************************/ + void dcMotorBrake(motor_channel_type_t chl); + + /************************************************************* + Description + Stop the motor slowly. + Parameter + chl: MOTOR_CHA or MOTOR_CHB + Return + Null. + *************************************************************/ + void dcMotorStop(motor_channel_type_t chl); + + /************************************************************* + Description + Drive a stepper. + Parameter + mode: 4 driver mode: FULL_STEP,WAVE_DRIVE, HALF_STEP, MICRO_STEPPING, + for more information: https://en.wikipedia.org/wiki/Stepper_motor#/media/File:Drive.png + steps: The number of steps to run, range from -32768 to 32767. + When steps = 0, the stepper stops. + When steps > 0, the stepper runs clockwise. When steps < 0, the stepper runs anticlockwise. + rpm: Revolutions per minute, the speed of a stepper, range from 1 to 300. + Note that high rpm will lead to step lose, so rpm should not be larger than 150. + Return + Null. + *************************************************************/ + void stepperRun(stepper_mode_type_t mode, int16_t steps, uint16_t rpm); + + /************************************************************* + Description + Stop a stepper. + Parameter + Null. + Return + Null. + *************************************************************/ + void stepperStop(); + + // keeps moving(direction same as the last move, default to clockwise) + /************************************************************* + Description + Keep a stepper running. + Parameter + mode: 4 driver mode: FULL_STEP,WAVE_DRIVE, HALF_STEP, MICRO_STEPPING, + for more information: https://en.wikipedia.org/wiki/Stepper_motor#/media/File:Drive.png + rpm: Revolutions per minute, the speed of a stepper, range from 1 to 300. + Note that high rpm will lead to step lose, so rpm should not be larger than 150. + is_cw: Set the running direction, true for clockwise and false for anti-clockwise. + + Return + Null. + *************************************************************/ + void stepperKeepRun(stepper_mode_type_t mode, uint16_t rpm, bool is_cw); + + private: + uint8_t _addr; + uint8_t _buffer[16]; +}; + +#endif //__GROVE_MOTOR_DRIVER_TB6612FNG_H__ \ No newline at end of file diff --git a/portail_coulissant/porcou-arduino/I2CScanner/I2CScanner.ino b/portail_coulissant/porcou-arduino/I2CScanner/I2CScanner.ino new file mode 100644 index 0000000..27a491c --- /dev/null +++ b/portail_coulissant/porcou-arduino/I2CScanner/I2CScanner.ino @@ -0,0 +1,83 @@ + // -------------------------------------- +// i2c_scanner +// +// Version 1 +// This program (or code that looks like it) +// can be found in many places. +// For example on the Arduino.cc forum. +// The original author is not know. +// Version 2, Juni 2012, Using Arduino 1.0.1 +// Adapted to be as simple as possible by Arduino.cc user Krodal +// Version 3, Feb 26 2013 +// V3 by louarnold +// Version 4, March 3, 2013, Using Arduino 1.0.3 +// by Arduino.cc user Krodal. +// Changes by louarnold removed. +// Scanning addresses changed from 0...127 to 1...119, +// according to the i2c scanner by Nick Gammon +// https://www.gammon.com.au/forum/?id=10896 +// Version 5, March 28, 2013 +// As version 4, but address scans now to 127. +// A sensor seems to use address 120. +// Version 6, November 27, 2015. +// Added waiting for the Leonardo serial communication. +// +// +// This sketch tests the standard 7-bit addresses +// Devices with higher bit address might not be seen properly. +// + +#include + + +void setup() +{ + Wire.begin(); + + Serial.begin(9600); + while (!Serial); // Leonardo: wait for serial monitor + Serial.println("\nI2C Scanner"); +} + + +void loop() +{ + byte error, address; + int nDevices; + + Serial.println("Scanning..."); + + nDevices = 0; + for(address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print("I2C device found at address 0x"); + if (address<16) + Serial.print("0"); + Serial.print(address,HEX); + Serial.println(" !"); + + nDevices++; + } + else if (error==4) + { + Serial.print("Unknown error at address 0x"); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + else + Serial.println("done\n"); + + delay(5000); // wait 5 seconds for next scan +} \ No newline at end of file diff --git a/portail_coulissant/porcou-arduino/I2Cdev.cpp b/portail_coulissant/porcou-arduino/I2Cdev.cpp new file mode 100755 index 0000000..7f52fe0 --- /dev/null +++ b/portail_coulissant/porcou-arduino/I2Cdev.cpp @@ -0,0 +1,1466 @@ +// I2Cdev library collection - Main I2C device class +// Abstracts bit and byte I2C R/W functions into a convenient class +// 6/9/2012 by Jeff Rowberg +// +// Changelog: +// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire +// - add compiler warnings when using outdated or IDE or limited I2Cdev implementation +// 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums) +// 2011-10-03 - added automatic Arduino version detection for ease of use +// 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications +// 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x) +// 2011-08-03 - added optional timeout parameter to read* methods to easily change from default +// 2011-08-02 - added support for 16-bit registers +// - fixed incorrect Doxygen comments on some methods +// - added timeout value for read operations (thanks mem @ Arduino forums) +// 2011-07-30 - changed read/write function structures to return success or byte counts +// - made all methods static for multi-device memory savings +// 2011-07-28 - initial release + +/* ============================================ + I2Cdev device library code is placed under the MIT license + Copyright (c) 2012 Jeff Rowberg + + 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 "I2Cdev.h" + +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + + #ifdef I2CDEV_IMPLEMENTATION_WARNINGS + #if ARDUINO < 100 + #warning Using outdated Arduino IDE with Wire library is functionally limiting. + #warning Arduino IDE v1.0.1+ with I2Cdev Fastwire implementation is recommended. + #warning This I2Cdev implementation does not support: + #warning - Repeated starts conditions + #warning - Timeout detection (some Wire requests block forever) + #elif ARDUINO == 100 + #warning Using outdated Arduino IDE with Wire library is functionally limiting. + #warning Arduino IDE v1.0.1+ with I2Cdev Fastwire implementation is recommended. + #warning This I2Cdev implementation does not support: + #warning - Repeated starts conditions + #warning - Timeout detection (some Wire requests block forever) + #elif ARDUINO > 100 + /* + #warning Using current Arduino IDE with Wire library is functionally limiting. + #warning Arduino IDE v1.0.1+ with I2CDEV_BUILTIN_FASTWIRE implementation is recommended. + #warning This I2Cdev implementation does not support: + #warning - Timeout detection (some Wire requests block forever) + */ + #endif + #endif + +#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE + + #error The I2CDEV_BUILTIN_FASTWIRE implementation is known to be broken right now. Patience, Iago! + +#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE + + #ifdef I2CDEV_IMPLEMENTATION_WARNINGS + #warning Using I2CDEV_BUILTIN_NBWIRE implementation may adversely affect interrupt detection. + #warning This I2Cdev implementation does not support: + #warning - Repeated starts conditions + #endif + + // NBWire implementation based heavily on code by Gene Knight + // Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html + // Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html + TwoWire Wire; + +#endif + +/** Default constructor. +*/ +I2Cdev::I2Cdev() { +} + +/** Read a single bit from an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to read from + @param bitNum Bit position to read (0-7) + @param data Container for single bit value + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Status of read operation (true = success) +*/ +int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t* data, uint16_t timeout) { + uint8_t b; + uint8_t count = readByte(devAddr, regAddr, &b, timeout); + *data = b & (1 << bitNum); + return count; +} + +/** Read a single bit from a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to read from + @param bitNum Bit position to read (0-15) + @param data Container for single bit value + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Status of read operation (true = success) +*/ +int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t* data, uint16_t timeout) { + uint16_t b; + uint8_t count = readWord(devAddr, regAddr, &b, timeout); + *data = b & (1 << bitNum); + return count; +} + +/** Read multiple bits from an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to read from + @param bitStart First bit position to read (0-7) + @param length Number of bits to read (not more than 8) + @param data Container for right-aligned value (i.e. '101' read from any bitStart position will equal 0x05) + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Status of read operation (true = success) +*/ +int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t* data, + uint16_t timeout) { + // 01101001 read byte + // 76543210 bit numbers + // xxx args: bitStart=4, length=3 + // 010 masked + // -> 010 shifted + uint8_t count, b; + if ((count = readByte(devAddr, regAddr, &b, timeout)) != 0) { + uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); + b &= mask; + b >>= (bitStart - length + 1); + *data = b; + } + return count; +} + +/** Read multiple bits from a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to read from + @param bitStart First bit position to read (0-15) + @param length Number of bits to read (not more than 16) + @param data Container for right-aligned value (i.e. '101' read from any bitStart position will equal 0x05) + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Status of read operation (1 = success, 0 = failure, -1 = timeout) +*/ +int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t* data, + uint16_t timeout) { + // 1101011001101001 read byte + // fedcba9876543210 bit numbers + // xxx args: bitStart=12, length=3 + // 010 masked + // -> 010 shifted + uint8_t count; + uint16_t w; + if ((count = readWord(devAddr, regAddr, &w, timeout)) != 0) { + uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1); + w &= mask; + w >>= (bitStart - length + 1); + *data = w; + } + return count; +} + +/** Read single byte from an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to read from + @param data Container for byte value read from device + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Status of read operation (true = success) +*/ +int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint16_t timeout) { + return readBytes(devAddr, regAddr, 1, data, timeout); +} + +/** Read single word from a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to read from + @param data Container for word value read from device + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Status of read operation (true = success) +*/ +int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t* data, uint16_t timeout) { + return readWords(devAddr, regAddr, 1, data, timeout); +} + +/** Read multiple bytes from an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr First register regAddr to read from + @param length Number of bytes to read + @param data Buffer to store read data in + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Number of bytes read (-1 indicates failure) +*/ +int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data, uint16_t timeout) { + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print("I2C (0x"); + Serial.print(devAddr, HEX); + Serial.print(") reading "); + Serial.print(length, DEC); + Serial.print(" bytes from 0x"); + Serial.print(regAddr, HEX); + Serial.print("..."); + #endif + + int8_t count = 0; + uint32_t t1 = millis(); + + #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE) + + #if (ARDUINO < 100) + // Arduino v00xx (before v1.0), Wire library + + // I2C/TWI subsystem uses internal buffer that breaks with large data requests + // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // smaller chunks instead of all at once + for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) { + Wire.beginTransmission(devAddr); + Wire.send(regAddr); + Wire.endTransmission(); + Wire.beginTransmission(devAddr); + Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); + + for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { + data[count] = Wire.receive(); + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[count], HEX); + if (count + 1 < length) { + Serial.print(" "); + } + #endif + } + + Wire.endTransmission(); + } + #elif (ARDUINO == 100) + // Arduino v1.0.0, Wire library + // Adds standardized write() and read() stream methods instead of send() and receive() + + // I2C/TWI subsystem uses internal buffer that breaks with large data requests + // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // smaller chunks instead of all at once + for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) { + Wire.beginTransmission(devAddr); + Wire.write(regAddr); + Wire.endTransmission(); + Wire.beginTransmission(devAddr); + Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); + + for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { + data[count] = Wire.read(); + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[count], HEX); + if (count + 1 < length) { + Serial.print(" "); + } + #endif + } + + Wire.endTransmission(); + } + #elif (ARDUINO > 100) + // Arduino v1.0.1+, Wire library + // Adds official support for repeated start condition, yay! + + // I2C/TWI subsystem uses internal buffer that breaks with large data requests + // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // smaller chunks instead of all at once + for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) { + Wire.beginTransmission(devAddr); + Wire.write(regAddr); + Wire.endTransmission(); + Wire.beginTransmission(devAddr); + Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); + + for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { + data[count] = Wire.read(); + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[count], HEX); + if (count + 1 < length) { + Serial.print(" "); + } + #endif + } + + Wire.endTransmission(); + } + #endif + + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) + // Fastwire library (STILL UNDER DEVELOPMENT, NON-FUNCTIONAL!) + + // no loop required for fastwire + uint8_t status = Fastwire::readBuf(devAddr, regAddr, data, length); + if (status == 0) { + count = length; // success + } else { + count = -1; // error + } + + #endif + + // check for timeout + if (timeout > 0 && millis() - t1 >= timeout && count < length) { + count = -1; // timeout + } + + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(". Done ("); + Serial.print(count, DEC); + Serial.println(" read)."); + #endif + + return count; +} + +/** Read multiple words from a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr First register regAddr to read from + @param length Number of words to read + @param data Buffer to store read data in + @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) + @return Number of words read (0 indicates failure) +*/ +int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data, uint16_t timeout) { + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print("I2C (0x"); + Serial.print(devAddr, HEX); + Serial.print(") reading "); + Serial.print(length, DEC); + Serial.print(" words from 0x"); + Serial.print(regAddr, HEX); + Serial.print("..."); + #endif + + int8_t count = 0; + uint32_t t1 = millis(); + + #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE) + + #if (ARDUINO < 100) + // Arduino v00xx (before v1.0), Wire library + + // I2C/TWI subsystem uses internal buffer that breaks with large data requests + // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // smaller chunks instead of all at once + for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) { + Wire.beginTransmission(devAddr); + Wire.send(regAddr); + Wire.endTransmission(); + Wire.beginTransmission(devAddr); + Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes + + bool msb = true; // starts with MSB, then LSB + for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { + if (msb) { + // first byte is bits 15-8 (MSb=15) + data[count] = Wire.receive() << 8; + } else { + // second byte is bits 7-0 (LSb=0) + data[count] |= Wire.receive(); + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[count], HEX); + if (count + 1 < length) { + Serial.print(" "); + } + #endif + count++; + } + msb = !msb; + } + + Wire.endTransmission(); + } + #elif (ARDUINO == 100) + // Arduino v1.0.0, Wire library + // Adds standardized write() and read() stream methods instead of send() and receive() + + // I2C/TWI subsystem uses internal buffer that breaks with large data requests + // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // smaller chunks instead of all at once + for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) { + Wire.beginTransmission(devAddr); + Wire.write(regAddr); + Wire.endTransmission(); + Wire.beginTransmission(devAddr); + Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes + + bool msb = true; // starts with MSB, then LSB + for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { + if (msb) { + // first byte is bits 15-8 (MSb=15) + data[count] = Wire.read() << 8; + } else { + // second byte is bits 7-0 (LSb=0) + data[count] |= Wire.read(); + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[count], HEX); + if (count + 1 < length) { + Serial.print(" "); + } + #endif + count++; + } + msb = !msb; + } + + Wire.endTransmission(); + } + #elif (ARDUINO > 100) + // Arduino v1.0.1+, Wire library + // Adds official support for repeated start condition, yay! + + // I2C/TWI subsystem uses internal buffer that breaks with large data requests + // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // smaller chunks instead of all at once + for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) { + Wire.beginTransmission(devAddr); + Wire.write(regAddr); + Wire.endTransmission(); + Wire.beginTransmission(devAddr); + Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes + + bool msb = true; // starts with MSB, then LSB + for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { + if (msb) { + // first byte is bits 15-8 (MSb=15) + data[count] = Wire.read() << 8; + } else { + // second byte is bits 7-0 (LSb=0) + data[count] |= Wire.read(); + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[count], HEX); + if (count + 1 < length) { + Serial.print(" "); + } + #endif + count++; + } + msb = !msb; + } + + Wire.endTransmission(); + } + #endif + + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) + // Fastwire library (STILL UNDER DEVELOPMENT, NON-FUNCTIONAL!) + + // no loop required for fastwire + uint16_t intermediate[(uint8_t)length]; + uint8_t status = Fastwire::readBuf(devAddr, regAddr, (uint8_t*)intermediate, (uint8_t)(length * 2)); + if (status == 0) { + count = length; // success + for (uint8_t i = 0; i < length; i++) { + data[i] = (intermediate[2 * i] << 8) | intermediate[2 * i + 1]; + } + } else { + count = -1; // error + } + + #endif + + if (timeout > 0 && millis() - t1 >= timeout && count < length) { + count = -1; // timeout + } + + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(". Done ("); + Serial.print(count, DEC); + Serial.println(" read)."); + #endif + + return count; +} + +/** write a single bit in an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to write to + @param bitNum Bit position to write (0-7) + @param value New bit value to write + @return Status of operation (true = success) +*/ +bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data) { + uint8_t b; + readByte(devAddr, regAddr, &b); + b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum)); + return writeByte(devAddr, regAddr, b); +} + +/** write a single bit in a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to write to + @param bitNum Bit position to write (0-15) + @param value New bit value to write + @return Status of operation (true = success) +*/ +bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data) { + uint16_t w; + readWord(devAddr, regAddr, &w); + w = (data != 0) ? (w | (1 << bitNum)) : (w & ~(1 << bitNum)); + return writeWord(devAddr, regAddr, w); +} + +/** Write multiple bits in an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to write to + @param bitStart First bit position to write (0-7) + @param length Number of bits to write (not more than 8) + @param data Right-aligned value to write + @return Status of operation (true = success) +*/ +bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) { + // 010 value to write + // 76543210 bit numbers + // xxx args: bitStart=4, length=3 + // 00011100 mask byte + // 10101111 original value (sample) + // 10100011 original & ~mask + // 10101011 masked | value + uint8_t b; + if (readByte(devAddr, regAddr, &b) != 0) { + uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); + data <<= (bitStart - length + 1); // shift data into correct position + data &= mask; // zero all non-important bits in data + b &= ~(mask); // zero all important bits in existing byte + b |= data; // combine data with existing byte + return writeByte(devAddr, regAddr, b); + } else { + return false; + } +} + +/** Write multiple bits in a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr Register regAddr to write to + @param bitStart First bit position to write (0-15) + @param length Number of bits to write (not more than 16) + @param data Right-aligned value to write + @return Status of operation (true = success) +*/ +bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data) { + // 010 value to write + // fedcba9876543210 bit numbers + // xxx args: bitStart=12, length=3 + // 0001110000000000 mask byte + // 1010111110010110 original value (sample) + // 1010001110010110 original & ~mask + // 1010101110010110 masked | value + uint16_t w; + if (readWord(devAddr, regAddr, &w) != 0) { + uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); + data <<= (bitStart - length + 1); // shift data into correct position + data &= mask; // zero all non-important bits in data + w &= ~(mask); // zero all important bits in existing word + w |= data; // combine data with existing word + return writeWord(devAddr, regAddr, w); + } else { + return false; + } +} + +/** Write single byte to an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr Register address to write to + @param data New byte value to write + @return Status of operation (true = success) +*/ +bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) { + return writeBytes(devAddr, regAddr, 1, &data); +} + +/** Write single word to a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr Register address to write to + @param data New word value to write + @return Status of operation (true = success) +*/ +bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data) { + return writeWords(devAddr, regAddr, 1, &data); +} + +/** Write multiple bytes to an 8-bit device register. + @param devAddr I2C slave device address + @param regAddr First register address to write to + @param length Number of bytes to write + @param data Buffer to copy new data from + @return Status of operation (true = success) +*/ +bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data) { + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print("I2C (0x"); + Serial.print(devAddr, HEX); + Serial.print(") writing "); + Serial.print(length, DEC); + Serial.print(" bytes to 0x"); + Serial.print(regAddr, HEX); + Serial.print("..."); + #endif + uint8_t status = 0; + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) + Wire.beginTransmission(devAddr); + Wire.send((uint8_t) regAddr); // send address + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) + Wire.beginTransmission(devAddr); + Wire.write((uint8_t) regAddr); // send address + #endif + for (uint8_t i = 0; i < length; i++) { + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) + Wire.send((uint8_t) data[i]); + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) + Wire.write((uint8_t) data[i]); + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) + status = Fastwire::write(devAddr, regAddr, data[i]); + Serial.println(status); + #endif + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[i], HEX); + if (i + 1 < length) { + Serial.print(" "); + } + #endif + } + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) + Wire.endTransmission(); + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) + status = Wire.endTransmission(); + #endif + #ifdef I2CDEV_SERIAL_DEBUG + Serial.println(". Done."); + #endif + return status == 0; +} + +/** Write multiple words to a 16-bit device register. + @param devAddr I2C slave device address + @param regAddr First register address to write to + @param length Number of words to write + @param data Buffer to copy new data from + @return Status of operation (true = success) +*/ +bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data) { + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print("I2C (0x"); + Serial.print(devAddr, HEX); + Serial.print(") writing "); + Serial.print(length, DEC); + Serial.print(" words to 0x"); + Serial.print(regAddr, HEX); + Serial.print("..."); + #endif + uint8_t status = 0; + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) + Wire.beginTransmission(devAddr); + Wire.send(regAddr); // send address + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) + Wire.beginTransmission(devAddr); + Wire.write(regAddr); // send address + #endif + for (uint8_t i = 0; i < length * 2; i++) { + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) + Wire.send((uint8_t)(data[i++] >> 8)); // send MSB + Wire.send((uint8_t)data[i]); // send LSB + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) + Wire.write((uint8_t)(data[i++] >> 8)); // send MSB + Wire.write((uint8_t)data[i]); // send LSB + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) + status = Fastwire::write(devAddr, regAddr, (uint8_t)(data[i++] >> 8)); + status = Fastwire::write(devAddr, regAddr + 1, (uint8_t)data[i]); + #endif + #ifdef I2CDEV_SERIAL_DEBUG + Serial.print(data[i], HEX); + if (i + 1 < length) { + Serial.print(" "); + } + #endif + } + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) + Wire.endTransmission(); + #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) + status = Wire.endTransmission(); + #endif + #ifdef I2CDEV_SERIAL_DEBUG + Serial.println(". Done."); + #endif + return status == 0; +} + +/** Default timeout value for read operations. + Set this to 0 to disable timeout detection. +*/ +uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; + +#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE +/* + FastWire 0.2 + This is a library to help faster programs to read I2C devices. + Copyright(C) 2011 Francesco Ferrara + occhiobello at gmail dot com +*/ + +boolean Fastwire::waitInt() { + int l = 250; + while (!(TWCR & (1 << TWINT)) && l-- > 0); + return l > 0; +} + +void Fastwire::setup(int khz, boolean pullup) { + TWCR = 0; + #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) + // activate internal pull-ups for twi (PORTC bits 4 & 5) + // as per note from atmega8 manual pg167 + if (pullup) { + PORTC |= ((1 << 4) | (1 << 5)); + } else { + PORTC &= ~((1 << 4) | (1 << 5)); + } + #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) + // activate internal pull-ups for twi (PORTC bits 0 & 1) + if (pullup) { + PORTC |= ((1 << 0) | (1 << 1)); + } else { + PORTC &= ~((1 << 0) | (1 << 1)); + } + #else + // activate internal pull-ups for twi (PORTD bits 0 & 1) + // as per note from atmega128 manual pg204 + if (pullup) { + PORTD |= ((1 << 0) | (1 << 1)); + } else { + PORTD &= ~((1 << 0) | (1 << 1)); + } + #endif + + TWSR = 0; // no prescaler => prescaler = 1 + TWBR = ((16000L / khz) - 16) / 2; // change the I2C clock rate + TWCR = 1 << TWEN; // enable twi module, no interrupt +} + +byte Fastwire::write(byte device, byte address, byte value) { + byte twst, retry; + + retry = 2; + do { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); + if (!waitInt()) { + return 1; + } + twst = TWSR & 0xF8; + if (twst != TW_START && twst != TW_REP_START) { + return 2; + } + + TWDR = device & 0xFE; // send device address without read bit (1) + TWCR = (1 << TWINT) | (1 << TWEN); + if (!waitInt()) { + return 3; + } + twst = TWSR & 0xF8; + } while (twst == TW_MT_SLA_NACK && retry-- > 0); + if (twst != TW_MT_SLA_ACK) { + return 4; + } + + TWDR = address; // send data to the previously addressed device + TWCR = (1 << TWINT) | (1 << TWEN); + if (!waitInt()) { + return 5; + } + twst = TWSR & 0xF8; + if (twst != TW_MT_DATA_ACK) { + return 6; + } + + TWDR = value; // send data to the previously addressed device + TWCR = (1 << TWINT) | (1 << TWEN); + if (!waitInt()) { + return 7; + } + twst = TWSR & 0xF8; + if (twst != TW_MT_DATA_ACK) { + return 8; + } + + return 0; +} + +byte Fastwire::readBuf(byte device, byte address, byte* data, byte num) { + byte twst, retry; + + retry = 2; + do { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); + if (!waitInt()) { + return 16; + } + twst = TWSR & 0xF8; + if (twst != TW_START && twst != TW_REP_START) { + return 17; + } + + TWDR = device & 0xfe; // send device address to write + TWCR = (1 << TWINT) | (1 << TWEN); + if (!waitInt()) { + return 18; + } + twst = TWSR & 0xF8; + } while (twst == TW_MT_SLA_NACK && retry-- > 0); + if (twst != TW_MT_SLA_ACK) { + return 19; + } + + TWDR = address; // send data to the previously addressed device + TWCR = (1 << TWINT) | (1 << TWEN); + if (!waitInt()) { + return 20; + } + twst = TWSR & 0xF8; + if (twst != TW_MT_DATA_ACK) { + return 21; + } + + /***/ + + retry = 2; + do { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); + if (!waitInt()) { + return 22; + } + twst = TWSR & 0xF8; + if (twst != TW_START && twst != TW_REP_START) { + return 23; + } + + TWDR = device | 0x01; // send device address with the read bit (1) + TWCR = (1 << TWINT) | (1 << TWEN); + if (!waitInt()) { + return 24; + } + twst = TWSR & 0xF8; + } while (twst == TW_MR_SLA_NACK && retry-- > 0); + if (twst != TW_MR_SLA_ACK) { + return 25; + } + + for (uint8_t i = 0; i < num; i++) { + if (i == num - 1) { + TWCR = (1 << TWINT) | (1 << TWEN); + } else { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); + } + if (!waitInt()) { + return 26; + } + twst = TWSR & 0xF8; + if (twst != TW_MR_DATA_ACK && twst != TW_MR_DATA_NACK) { + return twst; + } + data[i] = TWDR; + } + + return 0; +} +#endif + +#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE +// NBWire implementation based heavily on code by Gene Knight +// Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html +// Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html + +/* + call this version 1.0 + + Offhand, the only funky part that I can think of is in nbrequestFrom, where the buffer + length and index are set *before* the data is actually read. The problem is that these + are variables local to the TwoWire object, and by the time we actually have read the + data, and know what the length actually is, we have no simple access to the object's + variables. The actual bytes read *is* given to the callback function, though. + + The ISR code for a slave receiver is commented out. I don't have that setup, and can't + verify it at this time. Save it for 2.0! + + The handling of the read and write processes here is much like in the demo sketch code: + the process is broken down into sequential functions, where each registers the next as a + callback, essentially. + + For example, for the Read process, twi_read00 just returns if TWI is not yet in a + ready state. When there's another interrupt, and the interface *is* ready, then it + sets up the read, starts it, and registers twi_read01 as the function to call after + the *next* interrupt. twi_read01, then, just returns if the interface is still in a + "reading" state. When the reading is done, it copies the information to the buffer, + cleans up, and calls the user-requested callback function with the actual number of + bytes read. + + The writing is similar. + + Questions, comments and problems can go to Gene@Telobot.com. + + Thumbs Up! + Gene Knight + +*/ + +uint8_t TwoWire::rxBuffer[NBWIRE_BUFFER_LENGTH]; +uint8_t TwoWire::rxBufferIndex = 0; +uint8_t TwoWire::rxBufferLength = 0; + +uint8_t TwoWire::txAddress = 0; +uint8_t TwoWire::txBuffer[NBWIRE_BUFFER_LENGTH]; +uint8_t TwoWire::txBufferIndex = 0; +uint8_t TwoWire::txBufferLength = 0; + +//uint8_t TwoWire::transmitting = 0; +void (*TwoWire::user_onRequest)(void); +void (*TwoWire::user_onReceive)(int); + +static volatile uint8_t twi_transmitting; +static volatile uint8_t twi_state; +static uint8_t twi_slarw; +static volatile uint8_t twi_error; +static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; +static volatile uint8_t twi_masterBufferIndex; +static uint8_t twi_masterBufferLength; +static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; +static volatile uint8_t twi_rxBufferIndex; +//static volatile uint8_t twi_Interrupt_Continue_Command; +static volatile uint8_t twi_Return_Value; +static volatile uint8_t twi_Done; +void (*twi_cbendTransmissionDone)(int); +void (*twi_cbreadFromDone)(int); + +void twi_init() { + // initialize state + twi_state = TWI_READY; + + // activate internal pull-ups for twi + // as per note from atmega8 manual pg167 + sbi(PORTC, 4); + sbi(PORTC, 5); + + // initialize twi prescaler and bit rate + cbi(TWSR, TWPS0); // TWI Status Register - Prescaler bits + cbi(TWSR, TWPS1); + + /* twi bit rate formula from atmega128 manual pg 204 + SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) + note: TWBR should be 10 or higher for master mode + It is 72 for a 16mhz Wiring board with 100kHz TWI */ + + TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2; // bitrate register + // enable twi module, acks, and twi interrupt + + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); + + /* TWEN - TWI Enable Bit + TWIE - TWI Interrupt Enable + TWEA - TWI Enable Acknowledge Bit + TWINT - TWI Interrupt Flag + TWSTA - TWI Start Condition + */ +} + +typedef struct { + uint8_t address; + uint8_t* data; + uint8_t length; + uint8_t wait; + uint8_t i; +} twi_Write_Vars; + +twi_Write_Vars* ptwv = 0; +static void (*fNextInterruptFunction)(void) = 0; + +void twi_Finish(byte bRetVal) { + if (ptwv) { + free(ptwv); + ptwv = 0; + } + twi_Done = 0xFF; + twi_Return_Value = bRetVal; + fNextInterruptFunction = 0; +} + +uint8_t twii_WaitForDone(uint16_t timeout) { + uint32_t endMillis = millis() + timeout; + while (!twi_Done && (timeout == 0 || millis() < endMillis)) { + continue; + } + return twi_Return_Value; +} + +void twii_SetState(uint8_t ucState) { + twi_state = ucState; +} + +void twii_SetError(uint8_t ucError) { + twi_error = ucError ; +} + +void twii_InitBuffer(uint8_t ucPos, uint8_t ucLength) { + twi_masterBufferIndex = 0; + twi_masterBufferLength = ucLength; +} + +void twii_CopyToBuf(uint8_t* pData, uint8_t ucLength) { + uint8_t i; + for (i = 0; i < ucLength; ++i) { + twi_masterBuffer[i] = pData[i]; + } +} + +void twii_CopyFromBuf(uint8_t* pData, uint8_t ucLength) { + uint8_t i; + for (i = 0; i < ucLength; ++i) { + pData[i] = twi_masterBuffer[i]; + } +} + +void twii_SetSlaRW(uint8_t ucSlaRW) { + twi_slarw = ucSlaRW; +} + +void twii_SetStart() { + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); +} + +void twi_write01() { + if (TWI_MTX == twi_state) { + return; // blocking test + } + twi_transmitting = 0 ; + if (twi_error == 0xFF) { + twi_Finish(0); // success + } else if (twi_error == TW_MT_SLA_NACK) { + twi_Finish(2); // error: address send, nack received + } else if (twi_error == TW_MT_DATA_NACK) { + twi_Finish(3); // error: data send, nack received + } else { + twi_Finish(4); // other twi error + } + if (twi_cbendTransmissionDone) { + return twi_cbendTransmissionDone(twi_Return_Value); + } + return; +} + + +void twi_write00() { + if (TWI_READY != twi_state) { + return; // blocking test + } + if (TWI_BUFFER_LENGTH < ptwv -> length) { + twi_Finish(1); // end write with error 1 + return; + } + twi_Done = 0x00; // show as working + twii_SetState(TWI_MTX); // to transmitting + twii_SetError(0xFF); // to No Error + twii_InitBuffer(0, ptwv -> length); // pointer and length + twii_CopyToBuf(ptwv -> data, ptwv -> length); // get the data + twii_SetSlaRW((ptwv -> address << 1) | TW_WRITE); // write command + twii_SetStart(); // start the cycle + fNextInterruptFunction = twi_write01; // next routine + return twi_write01(); +} + +void twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait) { + uint8_t i; + ptwv = (twi_Write_Vars*)malloc(sizeof(twi_Write_Vars)); + ptwv -> address = address; + ptwv -> data = data; + ptwv -> length = length; + ptwv -> wait = wait; + fNextInterruptFunction = twi_write00; + return twi_write00(); +} + +void twi_read01() { + if (TWI_MRX == twi_state) { + return; // blocking test + } + if (twi_masterBufferIndex < ptwv -> length) { + ptwv -> length = twi_masterBufferIndex; + } + twii_CopyFromBuf(ptwv -> data, ptwv -> length); + twi_Finish(ptwv -> length); + if (twi_cbreadFromDone) { + return twi_cbreadFromDone(twi_Return_Value); + } + return; +} + +void twi_read00() { + if (TWI_READY != twi_state) { + return; // blocking test + } + if (TWI_BUFFER_LENGTH < ptwv -> length) { + twi_Finish(0); // error return + } + twi_Done = 0x00; // show as working + twii_SetState(TWI_MRX); // reading + twii_SetError(0xFF); // reset error + twii_InitBuffer(0, ptwv -> length - 1); // init to one less than length + twii_SetSlaRW((ptwv -> address << 1) | TW_READ); // read command + twii_SetStart(); // start cycle + fNextInterruptFunction = twi_read01; + return twi_read01(); +} + +void twi_readFrom(uint8_t address, uint8_t* data, uint8_t length) { + uint8_t i; + + ptwv = (twi_Write_Vars*)malloc(sizeof(twi_Write_Vars)); + ptwv -> address = address; + ptwv -> data = data; + ptwv -> length = length; + fNextInterruptFunction = twi_read00; + return twi_read00(); +} + +void twi_reply(uint8_t ack) { + // transmit master read ready signal, with or without ack + if (ack) { + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); + } else { + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); + } +} + +void twi_stop(void) { + // send stop condition + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); + + // wait for stop condition to be exectued on bus + // TWINT is not set after a stop condition! + while (TWCR & _BV(TWSTO)) { + continue; + } + + // update twi state + twi_state = TWI_READY; +} + +void twi_releaseBus(void) { + // release bus + TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); + + // update twi state + twi_state = TWI_READY; +} + +SIGNAL(TWI_vect) { + switch (TW_STATUS) { + // All Master + case TW_START: // sent start condition + case TW_REP_START: // sent repeated start condition + // copy device address and r/w bit to output register and ack + TWDR = twi_slarw; + twi_reply(1); + break; + + // Master Transmitter + case TW_MT_SLA_ACK: // slave receiver acked address + case TW_MT_DATA_ACK: // slave receiver acked data + // if there is data to send, send it, otherwise stop + if (twi_masterBufferIndex < twi_masterBufferLength) { + // copy data to output register and ack + TWDR = twi_masterBuffer[twi_masterBufferIndex++]; + twi_reply(1); + } else { + twi_stop(); + } + break; + + case TW_MT_SLA_NACK: // address sent, nack received + twi_error = TW_MT_SLA_NACK; + twi_stop(); + break; + + case TW_MT_DATA_NACK: // data sent, nack received + twi_error = TW_MT_DATA_NACK; + twi_stop(); + break; + + case TW_MT_ARB_LOST: // lost bus arbitration + twi_error = TW_MT_ARB_LOST; + twi_releaseBus(); + break; + + // Master Receiver + case TW_MR_DATA_ACK: // data received, ack sent + // put byte into buffer + twi_masterBuffer[twi_masterBufferIndex++] = TWDR; + + case TW_MR_SLA_ACK: // address sent, ack received + // ack if more bytes are expected, otherwise nack + if (twi_masterBufferIndex < twi_masterBufferLength) { + twi_reply(1); + } else { + twi_reply(0); + } + break; + + case TW_MR_DATA_NACK: // data received, nack sent + // put final byte into buffer + twi_masterBuffer[twi_masterBufferIndex++] = TWDR; + + case TW_MR_SLA_NACK: // address sent, nack received + twi_stop(); + break; + + // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case + + // Slave Receiver (NOT IMPLEMENTED YET) + /* + case TW_SR_SLA_ACK: // addressed, returned ack + case TW_SR_GCALL_ACK: // addressed generally, returned ack + case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack + case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack + // enter slave receiver mode + twi_state = TWI_SRX; + + // indicate that rx buffer can be overwritten and ack + twi_rxBufferIndex = 0; + twi_reply(1); + break; + + case TW_SR_DATA_ACK: // data received, returned ack + case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack + // if there is still room in the rx buffer + if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) { + // put byte in buffer and ack + twi_rxBuffer[twi_rxBufferIndex++] = TWDR; + twi_reply(1); + } else { + // otherwise nack + twi_reply(0); + } + break; + + case TW_SR_STOP: // stop or repeated start condition received + // put a null char after data if there's room + if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) { + twi_rxBuffer[twi_rxBufferIndex] = 0; + } + + // sends ack and stops interface for clock stretching + twi_stop(); + + // callback to user defined callback + twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); + + // since we submit rx buffer to "wire" library, we can reset it + twi_rxBufferIndex = 0; + + // ack future responses and leave slave receiver state + twi_releaseBus(); + break; + + case TW_SR_DATA_NACK: // data received, returned nack + case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack + // nack back at master + twi_reply(0); + break; + + // Slave Transmitter + case TW_ST_SLA_ACK: // addressed, returned ack + case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack + // enter slave transmitter mode + twi_state = TWI_STX; + + // ready the tx buffer index for iteration + twi_txBufferIndex = 0; + + // set tx buffer length to be zero, to verify if user changes it + twi_txBufferLength = 0; + + // request for txBuffer to be filled and length to be set + // note: user must call twi_transmit(bytes, length) to do this + twi_onSlaveTransmit(); + + // if they didn't change buffer & length, initialize it + if (0 == twi_txBufferLength) { + twi_txBufferLength = 1; + twi_txBuffer[0] = 0x00; + } + + // transmit first byte from buffer, fall through + + case TW_ST_DATA_ACK: // byte sent, ack returned + // copy data to output register + TWDR = twi_txBuffer[twi_txBufferIndex++]; + + // if there is more to send, ack, otherwise nack + if (twi_txBufferIndex < twi_txBufferLength) { + twi_reply(1); + } else { + twi_reply(0); + } + break; + + case TW_ST_DATA_NACK: // received nack, we are done + case TW_ST_LAST_DATA: // received ack, but we are done already! + // ack future responses + twi_reply(1); + // leave slave receiver state + twi_state = TWI_READY; + break; + */ + + // all + case TW_NO_INFO: // no state information + break; + + case TW_BUS_ERROR: // bus error, illegal stop/start + twi_error = TW_BUS_ERROR; + twi_stop(); + break; + } + + if (fNextInterruptFunction) { + return fNextInterruptFunction(); + } +} + +TwoWire::TwoWire() { } + +void TwoWire::begin(void) { + rxBufferIndex = 0; + rxBufferLength = 0; + + txBufferIndex = 0; + txBufferLength = 0; + + twi_init(); +} + +void TwoWire::beginTransmission(uint8_t address) { + //beginTransmission((uint8_t)address); + + // indicate that we are transmitting + twi_transmitting = 1; + + // set address of targeted slave + txAddress = address; + + // reset tx buffer iterator vars + txBufferIndex = 0; + txBufferLength = 0; +} + +uint8_t TwoWire::endTransmission(uint16_t timeout) { + // transmit buffer (blocking) + //int8_t ret = + twi_cbendTransmissionDone = NULL; + twi_writeTo(txAddress, txBuffer, txBufferLength, 1); + int8_t ret = twii_WaitForDone(timeout); + + // reset tx buffer iterator vars + txBufferIndex = 0; + txBufferLength = 0; + + // indicate that we are done transmitting + // twi_transmitting = 0; + return ret; +} + +void TwoWire::nbendTransmission(void (*function)(int)) { + twi_cbendTransmissionDone = function; + twi_writeTo(txAddress, txBuffer, txBufferLength, 1); + return; +} + +void TwoWire::send(uint8_t data) { + if (twi_transmitting) { + // in master transmitter mode + // don't bother if buffer is full + if (txBufferLength >= NBWIRE_BUFFER_LENGTH) { + return; + } + + // put byte in tx buffer + txBuffer[txBufferIndex] = data; + ++txBufferIndex; + + // update amount in buffer + txBufferLength = txBufferIndex; + } else { + // in slave send mode + // reply to master + //twi_transmit(&data, 1); + } +} + +uint8_t TwoWire::receive(void) { + // default to returning null char + // for people using with char strings + uint8_t value = 0; + + // get each successive byte on each call + if (rxBufferIndex < rxBufferLength) { + value = rxBuffer[rxBufferIndex]; + ++rxBufferIndex; + } + + return value; +} + +uint8_t TwoWire::requestFrom(uint8_t address, int quantity, uint16_t timeout) { + // clamp to buffer length + if (quantity > NBWIRE_BUFFER_LENGTH) { + quantity = NBWIRE_BUFFER_LENGTH; + } + + // perform blocking read into buffer + twi_cbreadFromDone = NULL; + twi_readFrom(address, rxBuffer, quantity); + uint8_t read = twii_WaitForDone(timeout); + + // set rx buffer iterator vars + rxBufferIndex = 0; + rxBufferLength = read; + + return read; +} + +void TwoWire::nbrequestFrom(uint8_t address, int quantity, void (*function)(int)) { + // clamp to buffer length + if (quantity > NBWIRE_BUFFER_LENGTH) { + quantity = NBWIRE_BUFFER_LENGTH; + } + + // perform blocking read into buffer + twi_cbreadFromDone = function; + twi_readFrom(address, rxBuffer, quantity); + //uint8_t read = twii_WaitForDone(); + + // set rx buffer iterator vars + //rxBufferIndex = 0; + //rxBufferLength = read; + + rxBufferIndex = 0; + rxBufferLength = quantity; // this is a hack + + return; //read; +} + +uint8_t TwoWire::available(void) { + return rxBufferLength - rxBufferIndex; +} + +#endif diff --git a/portail_coulissant/porcou-arduino/I2Cdev.h b/portail_coulissant/porcou-arduino/I2Cdev.h new file mode 100755 index 0000000..58402ce --- /dev/null +++ b/portail_coulissant/porcou-arduino/I2Cdev.h @@ -0,0 +1,274 @@ +// I2Cdev library collection - Main I2C device class header file +// Abstracts bit and byte I2C R/W functions into a convenient class +// 6/9/2012 by Jeff Rowberg +// +// Changelog: +// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire +// - add compiler warnings when using outdated or IDE or limited I2Cdev implementation +// 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums) +// 2011-10-03 - added automatic Arduino version detection for ease of use +// 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications +// 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x) +// 2011-08-03 - added optional timeout parameter to read* methods to easily change from default +// 2011-08-02 - added support for 16-bit registers +// - fixed incorrect Doxygen comments on some methods +// - added timeout value for read operations (thanks mem @ Arduino forums) +// 2011-07-30 - changed read/write function structures to return success or byte counts +// - made all methods static for multi-device memory savings +// 2011-07-28 - initial release + +/* ============================================ + I2Cdev device library code is placed under the MIT license + Copyright (c) 2012 Jeff Rowberg + + 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. + =============================================== +*/ + +#ifndef _I2CDEV_H_ +#define _I2CDEV_H_ + +// ----------------------------------------------------------------------------- +// I2C interface implementation setting +// ----------------------------------------------------------------------------- +#define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE + +// comment this out if you are using a non-optimal IDE/implementation setting +// but want the compiler to shut up about it +#define I2CDEV_IMPLEMENTATION_WARNINGS + +// ----------------------------------------------------------------------------- +// I2C interface implementation options +// ----------------------------------------------------------------------------- +#define I2CDEV_ARDUINO_WIRE 1 // Wire object from Arduino +#define I2CDEV_BUILTIN_NBWIRE 2 // Tweaked Wire object from Gene Knight's NBWire project +// ^^^ NBWire implementation is still buggy w/some interrupts! +#define I2CDEV_BUILTIN_FASTWIRE 3 // FastWire object from Francesco Ferrara's project +// ^^^ FastWire implementation in I2Cdev is INCOMPLETE! + +// ----------------------------------------------------------------------------- +// Arduino-style "Serial.print" debug constant (uncomment to enable) +// ----------------------------------------------------------------------------- +//#define I2CDEV_SERIAL_DEBUG + +#ifdef ARDUINO + #if ARDUINO < 100 + #include "WProgram.h" + #else + #include "Arduino.h" + #endif + #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE + #include + #ifndef BUFFER_LENGTH + #define BUFFER_LENGTH 32 + #endif + #endif +#else + #include "ArduinoWrapper.h" +#endif + +#if defined(ESP8266) || defined(ESP32) + #define min(x,y) _min(x,y) +#endif + +// 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];") +#define I2CDEV_DEFAULT_READ_TIMEOUT 1000 + +class I2Cdev { + public: + I2Cdev(); + + static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t* data, + uint16_t timeout = I2Cdev::readTimeout); + static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t* data, + uint16_t timeout = I2Cdev::readTimeout); + static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t* data, + uint16_t timeout = I2Cdev::readTimeout); + static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t* data, + uint16_t timeout = I2Cdev::readTimeout); + static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t* data, uint16_t timeout = I2Cdev::readTimeout); + static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t* data, uint16_t timeout = I2Cdev::readTimeout); + static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data, + uint16_t timeout = I2Cdev::readTimeout); + static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data, + uint16_t timeout = I2Cdev::readTimeout); + + static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data); + static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data); + static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); + static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data); + static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); + static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data); + static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data); + static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data); + + static uint16_t readTimeout; +}; + +#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE +////////////////////// +// FastWire 0.2 +// This is a library to help faster programs to read I2C devices. +// Copyright(C) 2011 +// Francesco Ferrara +////////////////////// + +/* Master */ +#define TW_START 0x08 +#define TW_REP_START 0x10 + +/* Master Transmitter */ +#define TW_MT_SLA_ACK 0x18 +#define TW_MT_SLA_NACK 0x20 +#define TW_MT_DATA_ACK 0x28 +#define TW_MT_DATA_NACK 0x30 +#define TW_MT_ARB_LOST 0x38 + +/* Master Receiver */ +#define TW_MR_ARB_LOST 0x38 +#define TW_MR_SLA_ACK 0x40 +#define TW_MR_SLA_NACK 0x48 +#define TW_MR_DATA_ACK 0x50 +#define TW_MR_DATA_NACK 0x58 + +#define TW_OK 0 +#define TW_ERROR 1 + +class Fastwire { + private: + static boolean waitInt(); + + public: + static void setup(int khz, boolean pullup); + static byte write(byte device, byte address, byte value); + static byte readBuf(byte device, byte address, byte* data, byte num); +}; +#endif + +#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE +// NBWire implementation based heavily on code by Gene Knight +// Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html +// Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html + +#define NBWIRE_BUFFER_LENGTH 32 + +class TwoWire { + private: + static uint8_t rxBuffer[]; + static uint8_t rxBufferIndex; + static uint8_t rxBufferLength; + + static uint8_t txAddress; + static uint8_t txBuffer[]; + static uint8_t txBufferIndex; + static uint8_t txBufferLength; + + // static uint8_t transmitting; + static void (*user_onRequest)(void); + static void (*user_onReceive)(int); + static void onRequestService(void); + static void onReceiveService(uint8_t*, int); + + public: + TwoWire(); + void begin(); + void begin(uint8_t); + void begin(int); + void beginTransmission(uint8_t); + //void beginTransmission(int); + uint8_t endTransmission(uint16_t timeout = 0); + void nbendTransmission(void (*function)(int)) ; + uint8_t requestFrom(uint8_t, int, uint16_t timeout = 0); + //uint8_t requestFrom(int, int); + void nbrequestFrom(uint8_t, int, void (*function)(int)); + void send(uint8_t); + void send(uint8_t*, uint8_t); + //void send(int); + void send(char*); + uint8_t available(void); + uint8_t receive(void); + void onReceive(void (*)(int)); + void onRequest(void (*)(void)); +}; + +#define TWI_READY 0 +#define TWI_MRX 1 +#define TWI_MTX 2 +#define TWI_SRX 3 +#define TWI_STX 4 + +#define TW_WRITE 0 +#define TW_READ 1 + +#define TW_MT_SLA_NACK 0x20 +#define TW_MT_DATA_NACK 0x30 + +#define CPU_FREQ 16000000L +#define TWI_FREQ 100000L +#define TWI_BUFFER_LENGTH 32 + +/* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */ + +#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3)) +#define TW_STATUS (TWSR & TW_STATUS_MASK) +#define TW_START 0x08 +#define TW_REP_START 0x10 +#define TW_MT_SLA_ACK 0x18 +#define TW_MT_SLA_NACK 0x20 +#define TW_MT_DATA_ACK 0x28 +#define TW_MT_DATA_NACK 0x30 +#define TW_MT_ARB_LOST 0x38 +#define TW_MR_ARB_LOST 0x38 +#define TW_MR_SLA_ACK 0x40 +#define TW_MR_SLA_NACK 0x48 +#define TW_MR_DATA_ACK 0x50 +#define TW_MR_DATA_NACK 0x58 +#define TW_ST_SLA_ACK 0xA8 +#define TW_ST_ARB_LOST_SLA_ACK 0xB0 +#define TW_ST_DATA_ACK 0xB8 +#define TW_ST_DATA_NACK 0xC0 +#define TW_ST_LAST_DATA 0xC8 +#define TW_SR_SLA_ACK 0x60 +#define TW_SR_ARB_LOST_SLA_ACK 0x68 +#define TW_SR_GCALL_ACK 0x70 +#define TW_SR_ARB_LOST_GCALL_ACK 0x78 +#define TW_SR_DATA_ACK 0x80 +#define TW_SR_DATA_NACK 0x88 +#define TW_SR_GCALL_DATA_ACK 0x90 +#define TW_SR_GCALL_DATA_NACK 0x98 +#define TW_SR_STOP 0xA0 +#define TW_NO_INFO 0xF8 +#define TW_BUS_ERROR 0x00 + +//#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr)) +//#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr)) + +#ifndef sbi // set bit + #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif // sbi + +#ifndef cbi // clear bit + #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#endif // cbi + +extern TwoWire Wire; + +#endif // I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE + +#endif /* _I2CDEV_H_ */ diff --git a/portail_coulissant/porcou-arduino/StandardFirmata/LICENSE.txt b/portail_coulissant/porcou-arduino/StandardFirmata/LICENSE.txt new file mode 100644 index 0000000..77cec6d --- /dev/null +++ b/portail_coulissant/porcou-arduino/StandardFirmata/LICENSE.txt @@ -0,0 +1,458 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + diff --git a/portail_coulissant/porcou-arduino/StandardFirmata/StandardFirmata.ino b/portail_coulissant/porcou-arduino/StandardFirmata/StandardFirmata.ino new file mode 100644 index 0000000..0e05a81 --- /dev/null +++ b/portail_coulissant/porcou-arduino/StandardFirmata/StandardFirmata.ino @@ -0,0 +1,824 @@ +/* + Firmata is a generic protocol for communicating with microcontrollers + from software on a host computer. It is intended to work with + any host computer software package. + + To download a host software package, please click on the following link + to open the list of Firmata client libraries in your default browser. + + https://github.com/firmata/arduino#firmata-client-libraries + + Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. + Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. + Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. + Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See file LICENSE.txt for further informations on licensing terms. + + Last updated August 17th, 2017 +*/ + +#include +#include +#include + +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define I2C_END_TX_MASK B01000000 +#define I2C_STOP_TX 1 +#define I2C_RESTART_TX 0 +#define I2C_MAX_QUERIES 8 +#define I2C_REGISTER_NOT_SPECIFIED -1 + +// the minimum interval for sampling analog input +#define MINIMUM_SAMPLING_INTERVAL 1 + + +/*============================================================================== + * GLOBAL VARIABLES + *============================================================================*/ + +#ifdef FIRMATA_SERIAL_FEATURE +SerialFirmata serialFeature; +#endif + +/* analog inputs */ +int analogInputsToReport = 0; // bitwise array to store pin reporting + +/* digital input ports */ +byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence +byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent + +/* pins configuration */ +byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else + +/* timer variables */ +unsigned long currentMillis; // store the current value from millis() +unsigned long previousMillis; // for comparison with currentMillis +unsigned int samplingInterval = 19; // how often to run the main loop (in ms) + +/* i2c data */ +struct i2c_device_info { + byte addr; + int reg; + byte bytes; + byte stopTX; +}; + +/* for i2c read continuous more */ +i2c_device_info query[I2C_MAX_QUERIES]; + +byte i2cRxData[64]; +boolean isI2CEnabled = false; +signed char queryIndex = -1; +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; + +Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte detachedServos[MAX_SERVOS]; +byte detachedServoCount = 0; +byte servoCount = 0; + +boolean isResetting = false; + +// Forward declare a few functions to avoid compiler errors with older versions +// of the Arduino IDE. +void setPinModeCallback(byte, int); +void reportAnalogCallback(byte analogPin, int value); +void sysexCallback(byte, byte, byte*); + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + +/*============================================================================== + * FUNCTIONS + *============================================================================*/ + +void attachServo(byte pin, int minPulse, int maxPulse) +{ + if (servoCount < MAX_SERVOS) { + // reuse indexes of detached servos until all have been reallocated + if (detachedServoCount > 0) { + servoPinMap[pin] = detachedServos[detachedServoCount - 1]; + if (detachedServoCount > 0) detachedServoCount--; + } else { + servoPinMap[pin] = servoCount; + servoCount++; + } + if (minPulse > 0 && maxPulse > 0) { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + } else { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + } + } else { + Firmata.sendString("Max servos attached"); + } +} + +void detachServo(byte pin) +{ + servos[servoPinMap[pin]].detach(); + // if we're detaching the last servo, decrement the count + // otherwise store the index of the detached servo + if (servoPinMap[pin] == servoCount && servoCount > 0) { + servoCount--; + } else if (servoCount > 0) { + // keep track of detached servos because we want to reuse their indexes + // before incrementing the count of attached servos + detachedServoCount++; + detachedServos[detachedServoCount - 1] = servoPinMap[pin]; + } + + servoPinMap[pin] = 255; +} + +void enableI2CPins() +{ + byte i; + // is there a faster way to do this? would probaby require importing + // Arduino.h to get SCL and SDA pins + for (i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_I2C(i)) { + // mark pins as i2c so they are ignore in non i2c data requests + setPinModeCallback(i, PIN_MODE_I2C); + } + } + + isI2CEnabled = true; + + Wire.begin(); +} + +/* disable the i2c pins so they can be used for other functions */ +void disableI2CPins() { + isI2CEnabled = false; + // disable read continuous mode for all devices + queryIndex = -1; +} + +void readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX) { + // allow I2C requests that don't require a register read + // for example, some devices using an interrupt pin to signify new data available + // do not always require the register read so upon interrupt you call Wire.requestFrom() + if (theRegister != I2C_REGISTER_NOT_SPECIFIED) { + Wire.beginTransmission(address); + wireWrite((byte)theRegister); + Wire.endTransmission(stopTX); // default = true + // do not set a value of 0 + if (i2cReadDelayTime > 0) { + // delay is necessary for some devices such as WiiNunchuck + delayMicroseconds(i2cReadDelayTime); + } + } else { + theRegister = 0; // fill the register with a dummy value + } + + Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom + + // check to be sure correct number of bytes were returned by slave + if (numBytes < Wire.available()) { + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); + numBytes = Wire.available(); + } + + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + + for (int i = 0; i < numBytes && Wire.available(); i++) { + i2cRxData[2 + i] = wireRead(); + } + + // send slave address, register and received bytes + Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); +} + +void outputPort(byte portNumber, byte portValue, byte forceSend) +{ + // pins not configured as INPUT are cleared to zeros + portValue = portValue & portConfigInputs[portNumber]; + // only send if the value is different than previously sent + if (forceSend || previousPINs[portNumber] != portValue) { + Firmata.sendDigitalPort(portNumber, portValue); + previousPINs[portNumber] = portValue; + } +} + +/* ----------------------------------------------------------------------------- + * check all the active digital inputs for change of state, then add any events + * to the Serial output queue using Serial.print() */ +void checkDigitalInputs(void) +{ + /* Using non-looping code allows constants to be given to readPort(). + * The compiler will apply substantial optimizations if the inputs + * to readPort() are compile-time constants. */ + if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false); + if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false); + if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false); + if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false); + if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false); + if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false); + if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false); + if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false); + if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false); + if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false); + if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false); + if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false); + if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false); + if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false); + if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false); + if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); +} + +// ----------------------------------------------------------------------------- +/* sets the pin mode to the correct state and sets the relevant bits in the + * two bit-arrays that track Digital I/O and PWM status + */ +void setPinModeCallback(byte pin, int mode) +{ + if (Firmata.getPinMode(pin) == PIN_MODE_IGNORE) + return; + + if (Firmata.getPinMode(pin) == PIN_MODE_I2C && isI2CEnabled && mode != PIN_MODE_I2C) { + // disable i2c so pins can be used for other functions + // the following if statements should reconfigure the pins properly + disableI2CPins(); + } + if (IS_PIN_DIGITAL(pin) && mode != PIN_MODE_SERVO) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + } + if (IS_PIN_ANALOG(pin)) { + reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0); // turn on/off reporting + } + if (IS_PIN_DIGITAL(pin)) { + if (mode == INPUT || mode == PIN_MODE_PULLUP) { + portConfigInputs[pin / 8] |= (1 << (pin & 7)); + } else { + portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); + } + } + Firmata.setPinState(pin, 0); + switch (mode) { + case PIN_MODE_ANALOG: + if (IS_PIN_ANALOG(pin)) { + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver +#if ARDUINO <= 100 + // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups +#endif + } + Firmata.setPinMode(pin, PIN_MODE_ANALOG); + } + break; + case INPUT: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver +#if ARDUINO <= 100 + // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups +#endif + Firmata.setPinMode(pin, INPUT); + } + break; + case PIN_MODE_PULLUP: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP); + Firmata.setPinMode(pin, PIN_MODE_PULLUP); + Firmata.setPinState(pin, 1); + } + break; + case OUTPUT: + if (IS_PIN_DIGITAL(pin)) { + if (Firmata.getPinMode(pin) == PIN_MODE_PWM) { + // Disable PWM if pin mode was previously set to PWM. + digitalWrite(PIN_TO_DIGITAL(pin), LOW); + } + pinMode(PIN_TO_DIGITAL(pin), OUTPUT); + Firmata.setPinMode(pin, OUTPUT); + } + break; + case PIN_MODE_PWM: + if (IS_PIN_PWM(pin)) { + pinMode(PIN_TO_PWM(pin), OUTPUT); + analogWrite(PIN_TO_PWM(pin), 0); + Firmata.setPinMode(pin, PIN_MODE_PWM); + } + break; + case PIN_MODE_SERVO: + if (IS_PIN_DIGITAL(pin)) { + Firmata.setPinMode(pin, PIN_MODE_SERVO); + if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { + // pass -1 for min and max pulse values to use default values set + // by Servo library + attachServo(pin, -1, -1); + } + } + break; + case PIN_MODE_I2C: + if (IS_PIN_I2C(pin)) { + // mark the pin as i2c + // the user must call I2C_CONFIG to enable I2C for a device + Firmata.setPinMode(pin, PIN_MODE_I2C); + } + break; + case PIN_MODE_SERIAL: +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handlePinMode(pin, PIN_MODE_SERIAL); +#endif + break; + default: + Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM + } + // TODO: save status to EEPROM here, if changed +} + +/* + * Sets the value of an individual pin. Useful if you want to set a pin value but + * are not tracking the digital port state. + * Can only be used on pins configured as OUTPUT. + * Cannot be used to enable pull-ups on Digital INPUT pins. + */ +void setPinValueCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS && IS_PIN_DIGITAL(pin)) { + if (Firmata.getPinMode(pin) == OUTPUT) { + Firmata.setPinState(pin, value); + digitalWrite(PIN_TO_DIGITAL(pin), value); + } + } +} + +void analogWriteCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS) { + switch (Firmata.getPinMode(pin)) { + case PIN_MODE_SERVO: + if (IS_PIN_DIGITAL(pin)) + servos[servoPinMap[pin]].write(value); + Firmata.setPinState(pin, value); + break; + case PIN_MODE_PWM: + if (IS_PIN_PWM(pin)) + analogWrite(PIN_TO_PWM(pin), value); + Firmata.setPinState(pin, value); + break; + } + } +} + +void digitalWriteCallback(byte port, int value) +{ + byte pin, lastPin, pinValue, mask = 1, pinWriteMask = 0; + + if (port < TOTAL_PORTS) { + // create a mask of the pins on this port that are writable. + lastPin = port * 8 + 8; + if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS; + for (pin = port * 8; pin < lastPin; pin++) { + // do not disturb non-digital pins (eg, Rx & Tx) + if (IS_PIN_DIGITAL(pin)) { + // do not touch pins in PWM, ANALOG, SERVO or other modes + if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) { + pinValue = ((byte)value & mask) ? 1 : 0; + if (Firmata.getPinMode(pin) == OUTPUT) { + pinWriteMask |= mask; + } else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) { + // only handle INPUT here for backwards compatibility +#if ARDUINO > 100 + pinMode(pin, INPUT_PULLUP); +#else + // only write to the INPUT pin to enable pullups if Arduino v1.0.0 or earlier + pinWriteMask |= mask; +#endif + } + Firmata.setPinState(pin, pinValue); + } + } + mask = mask << 1; + } + writePort(port, (byte)value, pinWriteMask); + } +} + + +// ----------------------------------------------------------------------------- +/* sets bits in a bit array (int) to toggle the reporting of the analogIns + */ +//void FirmataClass::setAnalogPinReporting(byte pin, byte state) { +//} +void reportAnalogCallback(byte analogPin, int value) +{ + if (analogPin < TOTAL_ANALOG_PINS) { + if (value == 0) { + analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); + } else { + analogInputsToReport = analogInputsToReport | (1 << analogPin); + // prevent during system reset or all analog pin values will be reported + // which may report noise for unconnected analog pins + if (!isResetting) { + // Send pin value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // TODO: save status to EEPROM here, if changed +} + +void reportDigitalCallback(byte port, int value) +{ + if (port < TOTAL_PORTS) { + reportPINs[port] = (byte)value; + // Send port value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); + } + // do not disable analog reporting on these 8 pins, to allow some + // pins used for digital, others analog. Instead, allow both types + // of reporting to be enabled, but check if the pin is configured + // as analog when sampling the analog inputs. Likewise, while + // scanning digital pins, portConfigInputs will mask off values from any + // pins configured as analog +} + +/*============================================================================== + * SYSEX-BASED commands + *============================================================================*/ + +void sysexCallback(byte command, byte argc, byte *argv) +{ + byte mode; + byte stopTX; + byte slaveAddress; + byte data; + int slaveRegister; + unsigned int delayTime; + + switch (command) { + case I2C_REQUEST: + mode = argv[1] & I2C_READ_WRITE_MODE_MASK; + if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { + Firmata.sendString("10-bit addressing not supported"); + return; + } + else { + slaveAddress = argv[0]; + } + + // need to invert the logic here since 0 will be default for client + // libraries that have not updated to add support for restart tx + if (argv[1] & I2C_END_TX_MASK) { + stopTX = I2C_RESTART_TX; + } + else { + stopTX = I2C_STOP_TX; // default + } + + switch (mode) { + case I2C_WRITE: + Wire.beginTransmission(slaveAddress); + for (byte i = 2; i < argc; i += 2) { + data = argv[i] + (argv[i + 1] << 7); + wireWrite(data); + } + Wire.endTransmission(); + delayMicroseconds(70); + break; + case I2C_READ: + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + readAndReportData(slaveAddress, (int)slaveRegister, data, stopTX); + break; + case I2C_READ_CONTINUOUSLY: + if ((queryIndex + 1) >= I2C_MAX_QUERIES) { + // too many queries, just ignore + Firmata.sendString("too many queries"); + break; + } + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + queryIndex++; + query[queryIndex].addr = slaveAddress; + query[queryIndex].reg = slaveRegister; + query[queryIndex].bytes = data; + query[queryIndex].stopTX = stopTX; + break; + case I2C_STOP_READING: + byte queryIndexToSkip; + // if read continuous mode is enabled for only 1 i2c device, disable + // read continuous reporting for that device + if (queryIndex <= 0) { + queryIndex = -1; + } else { + queryIndexToSkip = 0; + // if read continuous mode is enabled for multiple devices, + // determine which device to stop reading and remove it's data from + // the array, shifiting other array data to fill the space + for (byte i = 0; i < queryIndex + 1; i++) { + if (query[i].addr == slaveAddress) { + queryIndexToSkip = i; + break; + } + } + + for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { + if (i < I2C_MAX_QUERIES) { + query[i].addr = query[i + 1].addr; + query[i].reg = query[i + 1].reg; + query[i].bytes = query[i + 1].bytes; + query[i].stopTX = query[i + 1].stopTX; + } + } + queryIndex--; + } + break; + default: + break; + } + break; + case I2C_CONFIG: + delayTime = (argv[0] + (argv[1] << 7)); + + if (argc > 1 && delayTime > 0) { + i2cReadDelayTime = delayTime; + } + + if (!isI2CEnabled) { + enableI2CPins(); + } + + break; + case SERVO_CONFIG: + if (argc > 4) { + // these vars are here for clarity, they'll optimized away by the compiler + byte pin = argv[0]; + int minPulse = argv[1] + (argv[2] << 7); + int maxPulse = argv[3] + (argv[4] << 7); + + if (IS_PIN_DIGITAL(pin)) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + attachServo(pin, minPulse, maxPulse); + setPinModeCallback(pin, PIN_MODE_SERVO); + } + } + break; + case SAMPLING_INTERVAL: + if (argc > 1) { + samplingInterval = argv[0] + (argv[1] << 7); + if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { + samplingInterval = MINIMUM_SAMPLING_INTERVAL; + } + } else { + //Firmata.sendString("Not enough data"); + } + break; + case EXTENDED_ANALOG: + if (argc > 1) { + int val = argv[1]; + if (argc > 2) val |= (argv[2] << 7); + if (argc > 3) val |= (argv[3] << 14); + analogWriteCallback(argv[0], val); + } + break; + case CAPABILITY_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(CAPABILITY_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)INPUT); + Firmata.write(1); + Firmata.write((byte)PIN_MODE_PULLUP); + Firmata.write(1); + Firmata.write((byte)OUTPUT); + Firmata.write(1); + } + if (IS_PIN_ANALOG(pin)) { + Firmata.write(PIN_MODE_ANALOG); + Firmata.write(10); // 10 = 10-bit resolution + } + if (IS_PIN_PWM(pin)) { + Firmata.write(PIN_MODE_PWM); + Firmata.write(DEFAULT_PWM_RESOLUTION); + } + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(PIN_MODE_SERVO); + Firmata.write(14); + } + if (IS_PIN_I2C(pin)) { + Firmata.write(PIN_MODE_I2C); + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA + } +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handleCapability(pin); +#endif + Firmata.write(127); + } + Firmata.write(END_SYSEX); + break; + case PIN_STATE_QUERY: + if (argc > 0) { + byte pin = argv[0]; + Firmata.write(START_SYSEX); + Firmata.write(PIN_STATE_RESPONSE); + Firmata.write(pin); + if (pin < TOTAL_PINS) { + Firmata.write(Firmata.getPinMode(pin)); + Firmata.write((byte)Firmata.getPinState(pin) & 0x7F); + if (Firmata.getPinState(pin) & 0xFF80) Firmata.write((byte)(Firmata.getPinState(pin) >> 7) & 0x7F); + if (Firmata.getPinState(pin) & 0xC000) Firmata.write((byte)(Firmata.getPinState(pin) >> 14) & 0x7F); + } + Firmata.write(END_SYSEX); + } + break; + case ANALOG_MAPPING_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(ANALOG_MAPPING_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); + } + Firmata.write(END_SYSEX); + break; + + case SERIAL_MESSAGE: +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handleSysex(command, argc, argv); +#endif + break; + } +} + +/*============================================================================== + * SETUP() + *============================================================================*/ + +void systemResetCallback() +{ + isResetting = true; + + // initialize a defalt state + // TODO: option to load config from EEPROM instead of default + +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.reset(); +#endif + + if (isI2CEnabled) { + disableI2CPins(); + } + + for (byte i = 0; i < TOTAL_PORTS; i++) { + reportPINs[i] = false; // by default, reporting off + portConfigInputs[i] = 0; // until activated + previousPINs[i] = 0; + } + + for (byte i = 0; i < TOTAL_PINS; i++) { + // pins with analog capability default to analog input + // otherwise, pins default to digital output + if (IS_PIN_ANALOG(i)) { + // turns off pullup, configures everything + setPinModeCallback(i, PIN_MODE_ANALOG); + } else if (IS_PIN_DIGITAL(i)) { + // sets the output to 0, configures portConfigInputs + setPinModeCallback(i, OUTPUT); + } + + servoPinMap[i] = 255; + } + // by default, do not report any analog inputs + analogInputsToReport = 0; + + detachedServoCount = 0; + servoCount = 0; + + /* send digital inputs to set the initial state on the host computer, + * since once in the loop(), this firmware will only send on change */ + /* + TODO: this can never execute, since no pins default to digital input + but it will be needed when/if we support EEPROM stored config + for (byte i=0; i < TOTAL_PORTS; i++) { + outputPort(i, readPort(i, portConfigInputs[i]), true); + } + */ + isResetting = false; +} + +void setup() +{ + Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION); + + Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); + Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); + Firmata.attach(REPORT_ANALOG, reportAnalogCallback); + Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); + Firmata.attach(SET_PIN_MODE, setPinModeCallback); + Firmata.attach(SET_DIGITAL_PIN_VALUE, setPinValueCallback); + Firmata.attach(START_SYSEX, sysexCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); + + // to use a port other than Serial, such as Serial1 on an Arduino Leonardo or Mega, + // Call begin(baud) on the alternate serial port and pass it to Firmata to begin like this: + // Serial1.begin(57600); + // Firmata.begin(Serial1); + // However do not do this if you are using SERIAL_MESSAGE + + Firmata.begin(57600); + while (!Serial) { + ; // wait for serial port to connect. Needed for ATmega32u4-based boards and Arduino 101 + } + + systemResetCallback(); // reset to default config +} + +/*============================================================================== + * LOOP() + *============================================================================*/ +void loop() +{ + byte pin, analogPin; + + /* DIGITALREAD - as fast as possible, check for changes and output them to the + * FTDI buffer using Serial.print() */ + checkDigitalInputs(); + + /* STREAMREAD - processing incoming messagse as soon as possible, while still + * checking digital inputs. */ + while (Firmata.available()) + Firmata.processInput(); + + // TODO - ensure that Stream buffer doesn't go over 60 bytes + + currentMillis = millis(); + if (currentMillis - previousMillis > samplingInterval) { + previousMillis += samplingInterval; + /* ANALOGREAD - do all analogReads() at the configured sampling interval */ + for (pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) { + analogPin = PIN_TO_ANALOG(pin); + if (analogInputsToReport & (1 << analogPin)) { + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // report i2c data for all device with read continuous mode enabled + if (queryIndex > -1) { + for (byte i = 0; i < queryIndex + 1; i++) { + readAndReportData(query[i].addr, query[i].reg, query[i].bytes, query[i].stopTX); + } + } + } + +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.update(); +#endif +} diff --git a/portail_coulissant/porcou-arduino/dc_motor/dc_motor.ino b/portail_coulissant/porcou-arduino/dc_motor/dc_motor.ino new file mode 100644 index 0000000..a7d00d2 --- /dev/null +++ b/portail_coulissant/porcou-arduino/dc_motor/dc_motor.ino @@ -0,0 +1,37 @@ +#include "Grove_Motor_Driver_TB6612FNG.h" +#include + +MotorDriver motor; + +void setup() { + // join I2C bus (I2Cdev library doesn't do this automatically) + Wire.begin(); + Serial.begin(9600); + motor.init(); +} + +void loop() { + // drive 2 dc motors at speed=255, clockwise + Serial.println("run at speed=255"); + motor.dcMotorRun(MOTOR_CHA, 255); + motor.dcMotorRun(MOTOR_CHB, 255); + delay(1000); + + // brake + Serial.println("brake"); + motor.dcMotorBrake(MOTOR_CHA); + motor.dcMotorBrake(MOTOR_CHB); + delay(1000); + + // drive 2 dc motors at speed=200, anticlockwise + Serial.println("run at speed=-200"); + motor.dcMotorRun(MOTOR_CHA, -200); + motor.dcMotorRun(MOTOR_CHB, -200); + delay(1000); + + // stop 2 motors + Serial.println("stop"); + motor.dcMotorStop(MOTOR_CHA); + motor.dcMotorStop(MOTOR_CHB); + delay(1000); +} \ No newline at end of file diff --git a/portail_coulissant/porcou-arduino/dc_motor_stop/dc_motor_stop.ino b/portail_coulissant/porcou-arduino/dc_motor_stop/dc_motor_stop.ino new file mode 100644 index 0000000..0665c49 --- /dev/null +++ b/portail_coulissant/porcou-arduino/dc_motor_stop/dc_motor_stop.ino @@ -0,0 +1,21 @@ +#include "Grove_Motor_Driver_TB6612FNG.h" +#include + +MotorDriver motor; + +void setup() { + // join I2C bus (I2Cdev library doesn't do this automatically) + Wire.begin(); + Serial.begin(9600); + motor.init(); +} + +void loop() { + + + // stop 2 motors + Serial.println("stop"); + motor.dcMotorStop(MOTOR_CHA); + motor.dcMotorStop(MOTOR_CHB); + delay(1000); +} \ No newline at end of file diff --git a/portail_coulissant/porcou-arduino/lib/Grove_Motor_Driver_TB6612FNG.zip b/portail_coulissant/porcou-arduino/lib/Grove_Motor_Driver_TB6612FNG.zip new file mode 100644 index 0000000000000000000000000000000000000000..e143275d18a55e9d802a584ef87b071477118935 GIT binary patch literal 22564 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 O#K9thV&>vt(f +#include +#include + + +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_10BIT_ADDRESS_MODE_MASK B00100000 +#define I2C_END_TX_MASK B01000000 +#define I2C_STOP_TX 1 +#define I2C_RESTART_TX 0 +#define I2C_MAX_QUERIES 8 +#define I2C_REGISTER_NOT_SPECIFIED -1 + +// the minimum interval for sampling analog input +#define MINIMUM_SAMPLING_INTERVAL 1 + + +/*============================================================================== + * GLOBAL VARIABLES + *============================================================================*/ + +#ifdef FIRMATA_SERIAL_FEATURE +SerialFirmata serialFeature; +#endif + +/* analog inputs */ +int analogInputsToReport = 0; // bitwise array to store pin reporting + +/* digital input ports */ +byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence +byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent + +/* pins configuration */ +byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else + +/* timer variables */ +unsigned long currentMillis; // store the current value from millis() +unsigned long previousMillis; // for comparison with currentMillis +unsigned int samplingInterval = 19; // how often to run the main loop (in ms) + +/* i2c data */ +struct i2c_device_info { + byte addr; + int reg; + byte bytes; + byte stopTX; +}; + +/* for i2c read continuous more */ +i2c_device_info query[I2C_MAX_QUERIES]; + +byte i2cRxData[64]; +boolean isI2CEnabled = false; +signed char queryIndex = -1; +// default delay time between i2c read request and Wire.requestFrom() +unsigned int i2cReadDelayTime = 0; + +Servo servos[MAX_SERVOS]; +byte servoPinMap[TOTAL_PINS]; +byte detachedServos[MAX_SERVOS]; +byte detachedServoCount = 0; +byte servoCount = 0; + +boolean isResetting = false; + +MotorDriver motor; + +// Forward declare a few functions to avoid compiler errors with older versions +// of the Arduino IDE. +void setPinModeCallback(byte, int); +void reportAnalogCallback(byte analogPin, int value); +void sysexCallback(byte, byte, byte*); + +/* utility functions */ +void wireWrite(byte data) +{ +#if ARDUINO >= 100 + Wire.write((byte)data); +#else + Wire.send(data); +#endif +} + +byte wireRead(void) +{ +#if ARDUINO >= 100 + return Wire.read(); +#else + return Wire.receive(); +#endif +} + +/*============================================================================== + * FUNCTIONS + *============================================================================*/ + +void attachServo(byte pin, int minPulse, int maxPulse) +{ + if (servoCount < MAX_SERVOS) { + // reuse indexes of detached servos until all have been reallocated + if (detachedServoCount > 0) { + servoPinMap[pin] = detachedServos[detachedServoCount - 1]; + if (detachedServoCount > 0) detachedServoCount--; + } else { + servoPinMap[pin] = servoCount; + servoCount++; + } + if (minPulse > 0 && maxPulse > 0) { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); + } else { + servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); + } + } else { + Firmata.sendString("Max servos attached"); + } +} + +void detachServo(byte pin) +{ + servos[servoPinMap[pin]].detach(); + // if we're detaching the last servo, decrement the count + // otherwise store the index of the detached servo + if (servoPinMap[pin] == servoCount && servoCount > 0) { + servoCount--; + } else if (servoCount > 0) { + // keep track of detached servos because we want to reuse their indexes + // before incrementing the count of attached servos + detachedServoCount++; + detachedServos[detachedServoCount - 1] = servoPinMap[pin]; + } + + servoPinMap[pin] = 255; +} + +void enableI2CPins() +{ + byte i; + // is there a faster way to do this? would probaby require importing + // Arduino.h to get SCL and SDA pins + for (i = 0; i < TOTAL_PINS; i++) { + if (IS_PIN_I2C(i)) { + // mark pins as i2c so they are ignore in non i2c data requests + setPinModeCallback(i, PIN_MODE_I2C); + } + } + + isI2CEnabled = true; + + Wire.begin(); +} + +/* disable the i2c pins so they can be used for other functions */ +void disableI2CPins() { + isI2CEnabled = false; + // disable read continuous mode for all devices + queryIndex = -1; +} + +void readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX) { + // allow I2C requests that don't require a register read + // for example, some devices using an interrupt pin to signify new data available + // do not always require the register read so upon interrupt you call Wire.requestFrom() + if (theRegister != I2C_REGISTER_NOT_SPECIFIED) { + Wire.beginTransmission(address); + wireWrite((byte)theRegister); + Wire.endTransmission(stopTX); // default = true + // do not set a value of 0 + if (i2cReadDelayTime > 0) { + // delay is necessary for some devices such as WiiNunchuck + delayMicroseconds(i2cReadDelayTime); + } + } else { + theRegister = 0; // fill the register with a dummy value + } + + Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom + + // check to be sure correct number of bytes were returned by slave + if (numBytes < Wire.available()) { + Firmata.sendString("I2C: Too many bytes received"); + } else if (numBytes > Wire.available()) { + Firmata.sendString("I2C: Too few bytes received"); + numBytes = Wire.available(); + } + + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + + for (int i = 0; i < numBytes && Wire.available(); i++) { + i2cRxData[2 + i] = wireRead(); + } + + // send slave address, register and received bytes + Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); +} + +void outputPort(byte portNumber, byte portValue, byte forceSend) +{ + // pins not configured as INPUT are cleared to zeros + portValue = portValue & portConfigInputs[portNumber]; + // only send if the value is different than previously sent + if (forceSend || previousPINs[portNumber] != portValue) { + Firmata.sendDigitalPort(portNumber, portValue); + previousPINs[portNumber] = portValue; + } +} + +/* ----------------------------------------------------------------------------- + * check all the active digital inputs for change of state, then add any events + * to the Serial output queue using Serial.print() */ +void checkDigitalInputs(void) +{ + /* Using non-looping code allows constants to be given to readPort(). + * The compiler will apply substantial optimizations if the inputs + * to readPort() are compile-time constants. */ + if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false); + if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false); + if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false); + if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false); + if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false); + if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false); + if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false); + if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false); + if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false); + if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false); + if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false); + if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false); + if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false); + if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false); + if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false); + if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); +} + +// ----------------------------------------------------------------------------- +/* sets the pin mode to the correct state and sets the relevant bits in the + * two bit-arrays that track Digital I/O and PWM status + */ +void setPinModeCallback(byte pin, int mode) +{ + if (Firmata.getPinMode(pin) == PIN_MODE_IGNORE) + return; + + if (Firmata.getPinMode(pin) == PIN_MODE_I2C && isI2CEnabled && mode != PIN_MODE_I2C) { + // disable i2c so pins can be used for other functions + // the following if statements should reconfigure the pins properly + disableI2CPins(); + } + if (IS_PIN_DIGITAL(pin) && mode != PIN_MODE_SERVO) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + } + if (IS_PIN_ANALOG(pin)) { + reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0); // turn on/off reporting + } + if (IS_PIN_DIGITAL(pin)) { + if (mode == INPUT || mode == PIN_MODE_PULLUP) { + portConfigInputs[pin / 8] |= (1 << (pin & 7)); + } else { + portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); + } + } + Firmata.setPinState(pin, 0); + switch (mode) { + case PIN_MODE_ANALOG: + if (IS_PIN_ANALOG(pin)) { + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver +#if ARDUINO <= 100 + // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups +#endif + } + Firmata.setPinMode(pin, PIN_MODE_ANALOG); + } + break; + case INPUT: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver +#if ARDUINO <= 100 + // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 + digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups +#endif + Firmata.setPinMode(pin, INPUT); + } + break; + case PIN_MODE_PULLUP: + if (IS_PIN_DIGITAL(pin)) { + pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP); + Firmata.setPinMode(pin, PIN_MODE_PULLUP); + Firmata.setPinState(pin, 1); + } + break; + case OUTPUT: + if (IS_PIN_DIGITAL(pin)) { + if (Firmata.getPinMode(pin) == PIN_MODE_PWM) { + // Disable PWM if pin mode was previously set to PWM. + digitalWrite(PIN_TO_DIGITAL(pin), LOW); + } + pinMode(PIN_TO_DIGITAL(pin), OUTPUT); + Firmata.setPinMode(pin, OUTPUT); + } + break; + case PIN_MODE_PWM: + if (IS_PIN_PWM(pin)) { + pinMode(PIN_TO_PWM(pin), OUTPUT); + analogWrite(PIN_TO_PWM(pin), 0); + Firmata.setPinMode(pin, PIN_MODE_PWM); + } + break; + case PIN_MODE_SERVO: + if (IS_PIN_DIGITAL(pin)) { + Firmata.setPinMode(pin, PIN_MODE_SERVO); + if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { + // pass -1 for min and max pulse values to use default values set + // by Servo library + attachServo(pin, -1, -1); + } + } + break; + case PIN_MODE_I2C: + if (IS_PIN_I2C(pin)) { + // mark the pin as i2c + // the user must call I2C_CONFIG to enable I2C for a device + Firmata.setPinMode(pin, PIN_MODE_I2C); + } + break; + case PIN_MODE_SERIAL: +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handlePinMode(pin, PIN_MODE_SERIAL); +#endif + break; + default: + Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM + } + // TODO: save status to EEPROM here, if changed +} + +/* + * Sets the value of an individual pin. Useful if you want to set a pin value but + * are not tracking the digital port state. + * Can only be used on pins configured as OUTPUT. + * Cannot be used to enable pull-ups on Digital INPUT pins. + */ +void setPinValueCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS && IS_PIN_DIGITAL(pin)) { + if (Firmata.getPinMode(pin) == OUTPUT) { + Firmata.setPinState(pin, value); + digitalWrite(PIN_TO_DIGITAL(pin), value); + + // Gestion des moteurs (ajout : porcou-arduino.ino) + if (digitalRead(2) == LOW && digitalRead(3) == LOW) { + motor.dcMotorStop(MOTOR_CHA); + /* Serial.println("motor.dcMotorStop(MOTOR_CHA);"); */ + /* motor.dcMotorBrake(MOTOR_CHA); */ + } + if (digitalRead(2) == HIGH && digitalRead(3) == HIGH) { + motor.dcMotorStop(MOTOR_CHA); + /* Serial.println("motor.dcMotorStop(MOTOR_CHA);"); */ + } + if (digitalRead(2) == HIGH && digitalRead(3) == LOW) { + motor.dcMotorRun(MOTOR_CHA, 255); + /* Serial.println("motor.dcMotorRun(MOTOR_CHA, 255);"); */ + } + if (digitalRead(2) == LOW && digitalRead(3) == HIGH) { + motor.dcMotorRun(MOTOR_CHA, -255); + /* Serial.println("motor.dcMotorRun(MOTOR_CHA, -255);"); */ + } + } + } +} + +void analogWriteCallback(byte pin, int value) +{ + if (pin < TOTAL_PINS) { + switch (Firmata.getPinMode(pin)) { + case PIN_MODE_SERVO: + if (IS_PIN_DIGITAL(pin)) + servos[servoPinMap[pin]].write(value); + Firmata.setPinState(pin, value); + break; + case PIN_MODE_PWM: + if (IS_PIN_PWM(pin)) + analogWrite(PIN_TO_PWM(pin), value); + Firmata.setPinState(pin, value); + break; + } + } +} + +void digitalWriteCallback(byte port, int value) +{ + byte pin, lastPin, pinValue, mask = 1, pinWriteMask = 0; + + if (port < TOTAL_PORTS) { + // create a mask of the pins on this port that are writable. + lastPin = port * 8 + 8; + if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS; + for (pin = port * 8; pin < lastPin; pin++) { + // do not disturb non-digital pins (eg, Rx & Tx) + if (IS_PIN_DIGITAL(pin)) { + // do not touch pins in PWM, ANALOG, SERVO or other modes + if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) { + pinValue = ((byte)value & mask) ? 1 : 0; + if (Firmata.getPinMode(pin) == OUTPUT) { + pinWriteMask |= mask; + } else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) { + // only handle INPUT here for backwards compatibility +#if ARDUINO > 100 + pinMode(pin, INPUT_PULLUP); +#else + // only write to the INPUT pin to enable pullups if Arduino v1.0.0 or earlier + pinWriteMask |= mask; +#endif + } + Firmata.setPinState(pin, pinValue); + } + } + mask = mask << 1; + } + writePort(port, (byte)value, pinWriteMask); + } +} + + +// ----------------------------------------------------------------------------- +/* sets bits in a bit array (int) to toggle the reporting of the analogIns + */ +//void FirmataClass::setAnalogPinReporting(byte pin, byte state) { +//} +void reportAnalogCallback(byte analogPin, int value) +{ + if (analogPin < TOTAL_ANALOG_PINS) { + if (value == 0) { + analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); + } else { + analogInputsToReport = analogInputsToReport | (1 << analogPin); + // prevent during system reset or all analog pin values will be reported + // which may report noise for unconnected analog pins + if (!isResetting) { + // Send pin value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // TODO: save status to EEPROM here, if changed +} + +void reportDigitalCallback(byte port, int value) +{ + if (port < TOTAL_PORTS) { + reportPINs[port] = (byte)value; + // Send port value immediately. This is helpful when connected via + // ethernet, wi-fi or bluetooth so pin states can be known upon + // reconnecting. + if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); + } + // do not disable analog reporting on these 8 pins, to allow some + // pins used for digital, others analog. Instead, allow both types + // of reporting to be enabled, but check if the pin is configured + // as analog when sampling the analog inputs. Likewise, while + // scanning digital pins, portConfigInputs will mask off values from any + // pins configured as analog +} + +/*============================================================================== + * SYSEX-BASED commands + *============================================================================*/ + +void sysexCallback(byte command, byte argc, byte *argv) +{ + byte mode; + byte stopTX; + byte slaveAddress; + byte data; + int slaveRegister; + unsigned int delayTime; + + switch (command) { + case I2C_REQUEST: + mode = argv[1] & I2C_READ_WRITE_MODE_MASK; + if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { + Firmata.sendString("10-bit addressing not supported"); + return; + } + else { + slaveAddress = argv[0]; + } + + // need to invert the logic here since 0 will be default for client + // libraries that have not updated to add support for restart tx + if (argv[1] & I2C_END_TX_MASK) { + stopTX = I2C_RESTART_TX; + } + else { + stopTX = I2C_STOP_TX; // default + } + + switch (mode) { + case I2C_WRITE: + Wire.beginTransmission(slaveAddress); + for (byte i = 2; i < argc; i += 2) { + data = argv[i] + (argv[i + 1] << 7); + wireWrite(data); + } + Wire.endTransmission(); + delayMicroseconds(70); + break; + case I2C_READ: + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + readAndReportData(slaveAddress, (int)slaveRegister, data, stopTX); + break; + case I2C_READ_CONTINUOUSLY: + if ((queryIndex + 1) >= I2C_MAX_QUERIES) { + // too many queries, just ignore + Firmata.sendString("too many queries"); + break; + } + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + } + else { + // a slave register is NOT specified + slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED; + data = argv[2] + (argv[3] << 7); // bytes to read + } + queryIndex++; + query[queryIndex].addr = slaveAddress; + query[queryIndex].reg = slaveRegister; + query[queryIndex].bytes = data; + query[queryIndex].stopTX = stopTX; + break; + case I2C_STOP_READING: + byte queryIndexToSkip; + // if read continuous mode is enabled for only 1 i2c device, disable + // read continuous reporting for that device + if (queryIndex <= 0) { + queryIndex = -1; + } else { + queryIndexToSkip = 0; + // if read continuous mode is enabled for multiple devices, + // determine which device to stop reading and remove it's data from + // the array, shifiting other array data to fill the space + for (byte i = 0; i < queryIndex + 1; i++) { + if (query[i].addr == slaveAddress) { + queryIndexToSkip = i; + break; + } + } + + for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { + if (i < I2C_MAX_QUERIES) { + query[i].addr = query[i + 1].addr; + query[i].reg = query[i + 1].reg; + query[i].bytes = query[i + 1].bytes; + query[i].stopTX = query[i + 1].stopTX; + } + } + queryIndex--; + } + break; + default: + break; + } + break; + case I2C_CONFIG: + delayTime = (argv[0] + (argv[1] << 7)); + + if (argc > 1 && delayTime > 0) { + i2cReadDelayTime = delayTime; + } + + if (!isI2CEnabled) { + enableI2CPins(); + } + + break; + case SERVO_CONFIG: + if (argc > 4) { + // these vars are here for clarity, they'll optimized away by the compiler + byte pin = argv[0]; + int minPulse = argv[1] + (argv[2] << 7); + int maxPulse = argv[3] + (argv[4] << 7); + + if (IS_PIN_DIGITAL(pin)) { + if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { + detachServo(pin); + } + attachServo(pin, minPulse, maxPulse); + setPinModeCallback(pin, PIN_MODE_SERVO); + } + } + break; + case SAMPLING_INTERVAL: + if (argc > 1) { + samplingInterval = argv[0] + (argv[1] << 7); + if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { + samplingInterval = MINIMUM_SAMPLING_INTERVAL; + } + } else { + //Firmata.sendString("Not enough data"); + } + break; + case EXTENDED_ANALOG: + if (argc > 1) { + int val = argv[1]; + if (argc > 2) val |= (argv[2] << 7); + if (argc > 3) val |= (argv[3] << 14); + analogWriteCallback(argv[0], val); + } + break; + case CAPABILITY_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(CAPABILITY_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_DIGITAL(pin)) { + Firmata.write((byte)INPUT); + Firmata.write(1); + Firmata.write((byte)PIN_MODE_PULLUP); + Firmata.write(1); + Firmata.write((byte)OUTPUT); + Firmata.write(1); + } + if (IS_PIN_ANALOG(pin)) { + Firmata.write(PIN_MODE_ANALOG); + Firmata.write(10); // 10 = 10-bit resolution + } + if (IS_PIN_PWM(pin)) { + Firmata.write(PIN_MODE_PWM); + Firmata.write(DEFAULT_PWM_RESOLUTION); + } + if (IS_PIN_DIGITAL(pin)) { + Firmata.write(PIN_MODE_SERVO); + Firmata.write(14); + } + if (IS_PIN_I2C(pin)) { + Firmata.write(PIN_MODE_I2C); + Firmata.write(1); // TODO: could assign a number to map to SCL or SDA + } +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handleCapability(pin); +#endif + Firmata.write(127); + } + Firmata.write(END_SYSEX); + break; + case PIN_STATE_QUERY: + if (argc > 0) { + byte pin = argv[0]; + Firmata.write(START_SYSEX); + Firmata.write(PIN_STATE_RESPONSE); + Firmata.write(pin); + if (pin < TOTAL_PINS) { + Firmata.write(Firmata.getPinMode(pin)); + Firmata.write((byte)Firmata.getPinState(pin) & 0x7F); + if (Firmata.getPinState(pin) & 0xFF80) Firmata.write((byte)(Firmata.getPinState(pin) >> 7) & 0x7F); + if (Firmata.getPinState(pin) & 0xC000) Firmata.write((byte)(Firmata.getPinState(pin) >> 14) & 0x7F); + } + Firmata.write(END_SYSEX); + } + break; + case ANALOG_MAPPING_QUERY: + Firmata.write(START_SYSEX); + Firmata.write(ANALOG_MAPPING_RESPONSE); + for (byte pin = 0; pin < TOTAL_PINS; pin++) { + Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); + } + Firmata.write(END_SYSEX); + break; + + case SERIAL_MESSAGE: +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.handleSysex(command, argc, argv); +#endif + break; + } +} + +/*============================================================================== + * SETUP() + *============================================================================*/ + +void systemResetCallback() +{ + isResetting = true; + + // initialize a defalt state + // TODO: option to load config from EEPROM instead of default + +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.reset(); +#endif + + if (isI2CEnabled) { + disableI2CPins(); + } + + for (byte i = 0; i < TOTAL_PORTS; i++) { + reportPINs[i] = false; // by default, reporting off + portConfigInputs[i] = 0; // until activated + previousPINs[i] = 0; + } + + for (byte i = 0; i < TOTAL_PINS; i++) { + // pins with analog capability default to analog input + // otherwise, pins default to digital output + if (IS_PIN_ANALOG(i)) { + // turns off pullup, configures everything + setPinModeCallback(i, PIN_MODE_ANALOG); + } else if (IS_PIN_DIGITAL(i)) { + // sets the output to 0, configures portConfigInputs + setPinModeCallback(i, OUTPUT); + } + + servoPinMap[i] = 255; + } + // by default, do not report any analog inputs + analogInputsToReport = 0; + + detachedServoCount = 0; + servoCount = 0; + + /* send digital inputs to set the initial state on the host computer, + * since once in the loop(), this firmware will only send on change */ + /* + TODO: this can never execute, since no pins default to digital input + but it will be needed when/if we support EEPROM stored config + for (byte i=0; i < TOTAL_PORTS; i++) { + outputPort(i, readPort(i, portConfigInputs[i]), true); + } + */ + isResetting = false; +} + +void setup() +{ + Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION); + + Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); + Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); + Firmata.attach(REPORT_ANALOG, reportAnalogCallback); + Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); + Firmata.attach(SET_PIN_MODE, setPinModeCallback); + Firmata.attach(SET_DIGITAL_PIN_VALUE, setPinValueCallback); + Firmata.attach(START_SYSEX, sysexCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); + + // to use a port other than Serial, such as Serial1 on an Arduino Leonardo or Mega, + // Call begin(baud) on the alternate serial port and pass it to Firmata to begin like this: + // Serial1.begin(57600); + // Firmata.begin(Serial1); + // However do not do this if you are using SERIAL_MESSAGE + + Firmata.begin(57600); + while (!Serial) { + ; // wait for serial port to connect. Needed for ATmega32u4-based boards and Arduino 101 + } + + systemResetCallback(); // reset to default config + + motor.init(); // Gestion des moteurs (porcou-arduino.ino) +} + +/*============================================================================== + * LOOP() + *============================================================================*/ +void loop() +{ + byte pin, analogPin; + + /* DIGITALREAD - as fast as possible, check for changes and output them to the + * FTDI buffer using Serial.print() */ + checkDigitalInputs(); + + /* STREAMREAD - processing incoming messagse as soon as possible, while still + * checking digital inputs. */ + while (Firmata.available()) + Firmata.processInput(); + + // TODO - ensure that Stream buffer doesn't go over 60 bytes + + currentMillis = millis(); + if (currentMillis - previousMillis > samplingInterval) { + previousMillis += samplingInterval; + /* ANALOGREAD - do all analogReads() at the configured sampling interval */ + for (pin = 0; pin < TOTAL_PINS; pin++) { + if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) { + analogPin = PIN_TO_ANALOG(pin); + if (analogInputsToReport & (1 << analogPin)) { + Firmata.sendAnalog(analogPin, analogRead(analogPin)); + } + } + } + // report i2c data for all device with read continuous mode enabled + if (queryIndex > -1) { + for (byte i = 0; i < queryIndex + 1; i++) { + readAndReportData(query[i].addr, query[i].reg, query[i].bytes, query[i].stopTX); + } + } + } + + // Gestion des moteurs (ajout : porcou-arduino.ino) + + /* if (digitalRead(2) == LOW && digitalRead(3) == LOW) { */ + /* motor.dcMotorStop(MOTOR_CHA); */ + /* Serial.println("motor.dcMotorStop(MOTOR_CHA);"); */ + /* /\* motor.dcMotorBrake(MOTOR_CHA); *\/ */ + /* } */ + /* if (digitalRead(2) == HIGH && digitalRead(3) == HIGH) { */ + /* motor.dcMotorStop(MOTOR_CHA); */ + /* Serial.println("motor.dcMotorStop(MOTOR_CHA);"); */ + /* } */ + /* if (digitalRead(2) == HIGH && digitalRead(3) == LOW) { */ + /* motor.dcMotorRun(MOTOR_CHA, 255); */ + /* Serial.println("motor.dcMotorRun(MOTOR_CHA, 255);"); */ + /* } */ + /* if (digitalRead(2) == LOW && digitalRead(3) == HIGH) { */ + /* motor.dcMotorRun(MOTOR_CHA, -255); */ + /* Serial.println("motor.dcMotorRun(MOTOR_CHA, -255);"); */ + /* } */ + + +#ifdef FIRMATA_SERIAL_FEATURE + serialFeature.update(); +#endif +} diff --git a/portail_coulissant/porcou-arduino/serial/porcou-arduino-v1.ino b/portail_coulissant/porcou-arduino/serial/porcou-arduino-v1.ino new file mode 100644 index 0000000..93a5a94 --- /dev/null +++ b/portail_coulissant/porcou-arduino/serial/porcou-arduino-v1.ino @@ -0,0 +1,91 @@ +#include "Wire.h" +#include "Grove_Motor_Driver_TB6612FNG.h" +#include + +/****************************************************************************** + * porcou-arduino.ino + * @title: Programme pour la carte Arduino + * @project: Blender-EduTech - Jumeau numérique - Portail coulissant + * @lang: fr + * @authors: Philippe Roy + * @copyright: Copyright (C) 2024 Philippe Roy + * @license: GNU GPL + ******************************************************************************/ + +/****************************************************************************** + * Précautions : + * - Régler l'alimentation à 12V DC - 3 A maxi + ******************************************************************************/ + +/****************************************************************************** + * Communication serie + ******************************************************************************/ + +String serial_msg = ""; // Message +bool serial_msg_complet = false; // Flag de message complet + +/****************************************************************************** + * Initialisation + ******************************************************************************/ + +void setup() { + + // Liaison série + Serial.begin(115200); +} + +/****************************************************************************** + * Boucle principale + ******************************************************************************/ + +void loop() { + + /***** + * Lecture des capteur/boutons + *****/ + + /* accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz); */ + /* Axyz[0] = (double) ax / 16384; */ + /* Axyz[1] = (double) ay / 16384; */ + /* Axyz[2] = (double) az / 16384; */ + /* roll = asin(-Axyz[0]); */ + /* roll_deg = roll*57.3; */ + /* roll_txt = String(roll_deg); */ + /* pitch = -asin(Axyz[1]/cos(roll)); // position capteur (X vers la gauche, Y vers l'arriere, Z vers le haut) */ + /* pitch_deg = pitch*57.3; */ + /* pitch_txt = String(pitch_deg); */ + + /***** + * Capteur / boutons : Arduino -> UPBGE + *****/ + + Serial.print(roll_txt); + Serial.print(","); + Serial.print(pitch_txt); + Serial.println(); + + /***** + * Moteur / Led : UPBGE -> Arduino + *****/ + + if (serial_msg_complet) { + int xy= serial_msg.toInt(); // Message par chiffre : XY + + serial_msg = ""; + serial_msg_complet = false; + } +} + +/****************************************************************************** + * Évènement provoqué par UPBGE (via la liaison série) + ******************************************************************************/ + +void serialEvent() { + while (Serial.available()) { + char inChar = (char)Serial.read(); + serial_msg += inChar; + if (inChar == '\n') { + serial_msg_complet = true; + } + } +} diff --git a/portail_coulissant/porcou_cmd.py b/portail_coulissant/porcou_cmd.py index 40efc0f..887908e 100644 --- a/portail_coulissant/porcou_cmd.py +++ b/portail_coulissant/porcou_cmd.py @@ -43,10 +43,10 @@ from porcou_lib import * # Bibliothèque utilisateur du portail coulissant # Brochage du portail coulissant (Grove) brochage={ 'bp_ext' : ['d',6,'i'],'bp_int' : ['d',5,'i'], - 'fdc_o' : ['d',3,'i'],'fdc_f' : ['d',2,'i'], - 'mot_o' : ['d',7,'o'],'mot_f' : ['d',8,'o'], - 'gyr' : ['d',4,'o']} - # 'ir_emet' : ['d',8,'o'],'ir_recep' : ['d',7,'i']} + 'fdc_o' : ['a',1,'i'],'fdc_f' : ['a',0,'i'], + 'mot_o' : ['d',2,'o'],'mot_f' : ['d',3,'o'], + 'gyr' : ['d',4,'o'], + 'ir_emet' : ['d',8,'o'],'ir_recep' : ['d',7,'i']} ############################################################################### # Fonctions