From ec03b6a6e60fb723982666885cb5c4fe4ae0e52a Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Mon, 26 Sep 2011 20:03:46 -0500 Subject: [PATCH 01/67] Added to Git, the right way this time. Signed-off-by: Bill Porter --- EasyTransfer/EasyTransfer.cpp | 84 +++++++++++++++++ EasyTransfer/EasyTransfer.h | 59 ++++++++++++ .../EasyTransfer_2Way_wPot_Example.pde | 85 ++++++++++++++++++ .../EasyTransfer_2Way_wServo_Example.pde | 90 +++++++++++++++++++ .../EasyTransfer_RX_Example.pde | 40 +++++++++ .../EasyTransfer_TX_Example.pde | 43 +++++++++ EasyTransfer/keywords.txt | 23 +++++ README.txt | 0 8 files changed, 424 insertions(+) create mode 100644 EasyTransfer/EasyTransfer.cpp create mode 100644 EasyTransfer/EasyTransfer.h create mode 100644 EasyTransfer/Examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde create mode 100644 EasyTransfer/Examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde create mode 100644 EasyTransfer/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde create mode 100644 EasyTransfer/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde create mode 100644 EasyTransfer/keywords.txt create mode 100644 README.txt diff --git a/EasyTransfer/EasyTransfer.cpp b/EasyTransfer/EasyTransfer.cpp new file mode 100644 index 0000000..dd84ab6 --- /dev/null +++ b/EasyTransfer/EasyTransfer.cpp @@ -0,0 +1,84 @@ +#include "EasyTransfer.h" + + + + +//Captures address and size of struct +void EasyTransfer::begin(uint8_t * ptr, uint8_t length, HardwareSerial *theSerial){ +address = ptr; +size = length; +_serial = theSerial; +} + +//Sends out struct in binary, with header, length info and checksum +void EasyTransfer::sendData(){ + uint8_t CS = size; + _serial->print(0x06, BYTE); + _serial->print(0x85, BYTE); + _serial->print(size, BYTE); + for(int i = 0; iprint(*(address+i), BYTE); + } + _serial->print(CS); + +} + +boolean EasyTransfer::receiveData(){ + + //start off by looking for the header bytes. If they were already found in a previous call, skip it. + if(rx_len == 0){ + //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. + if(_serial->available() >= 3){ + //this will block until a 0x06 is found or buffer size becomes less then 3. + while(_serial->read() != 0x06) { + //This will trash any preamble junk in the serial buffer + //but we need to make sure there is enough in the buffer to process while we trash the rest + //if the buffer becomes too empty, we will escape and try again on the next call + if(_serial->available() < 3) + return false; + } + if (_serial->read() == 0x85){ + rx_len = _serial->read(); + //make sure the binary structs on both Arduinos are the same size. + if(rx_len != size){ + rx_len = 0; + return false; + } + } + } + } + + //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. + if(rx_len != 0){ + while(_serial->available() && rx_array_inx <= rx_len){ + rx_array[rx_array_inx++] = _serial->read(); + } + + if(rx_len == (rx_array_inx-1)){ + //seem to have got whole message + //last uint8_t is CS + calc_CS = rx_len; + for (int i = 0; i +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ +#ifndef EasyTransfer_h +#define EasyTransfer_h + + +//make it a little prettier on the front end. +#define details(name) (byte*)&name,sizeof(name) + +//Not neccessary, but just in case. +#include "WProgram.h" +#include "HardwareSerial.h" +//#include +#include +#include +#include +#include + +class EasyTransfer { +public: +void begin(uint8_t *, uint8_t, HardwareSerial *theSerial); +//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); +void sendData(); +boolean receiveData(); +private: +HardwareSerial *_serial; +//NewSoftSerial *_serial; +uint8_t * address; //address of struct +uint8_t size; //size of struct +uint8_t rx_len; //RX packet length according to the packet +uint8_t rx_array[255]; //RX packet parsing buffer +uint8_t rx_array_inx; //index for RX parsing buffer +uint8_t calc_CS; //calculated Chacksum +}; + + + +#endif \ No newline at end of file diff --git a/EasyTransfer/Examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde b/EasyTransfer/Examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde new file mode 100644 index 0000000..9c02885 --- /dev/null +++ b/EasyTransfer/Examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde @@ -0,0 +1,85 @@ +/*This is an example of the EasyTransfer Library 2way communications. + +The sketch is for the Arduino with a potentiometer attached to analog pin 0. + +This other Arduino has the servo attached to pin 9. +Both have a putton attached to pin 12 and output a status using the LED on pin 13. + +The idea is each arduino will read the status of the button attached to it, and send it +to the other Arduino, which will toggle it's LED based on the others button. The button +should connect pin 12 to ground when pushed. + +And the Arduino with the potentiometer will send it's value to the one with the servo. +The servo will move to the position based on the potentiometer. +*/ + + + +#include + +//create two objects +EasyTransfer ETin, ETout; + + +struct RECEIVE_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int buttonstate; +}; + +struct SEND_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int buttonstate; + int servoval; +}; + +//give a name to the group of data +RECEIVE_DATA_STRUCTURE rxdata; +SEND_DATA_STRUCTURE txdata; + + +void setup(){ + Serial.begin(9600); + //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. + ETin.begin(details(rxdata), &Serial); + ETout.begin(details(txdata), &Serial); + + pinMode(13, OUTPUT); + pinMode(12, INPUT); + //enable pull-up + digitalWrite(12, HIGH); + +} + +void loop(){ + + //first, lets read our potentiometer and button and store it in our data structure + txdata.servoval = analogRead(0); + + if(!digitalRead(12)) + txdata.buttonstate = HIGH; + else + txdata.buttonstate = LOW; + + //then we will go ahead and send that data out + ETout.sendData(); + + //there's a loop here so that we run the recieve function more often then the + //transmit function. This is important due to the slight differences in + //the clock speed of different Arduinos. If we didn't do this, messages + //would build up in the buffer and appear to cause a delay. + for(int i=0; i<5; i++){ + //remember, you could use an if() here to check for new data, this time it's not needed. + ETin.receiveData(); + + //set our LED on or off based on what we received from the other Arduino + digitalWrite(13, rxdata.buttonstate); + + //delay + delay(10); + } + + //delay for good measure + delay(10); +} diff --git a/EasyTransfer/Examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde b/EasyTransfer/Examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde new file mode 100644 index 0000000..22dc9e1 --- /dev/null +++ b/EasyTransfer/Examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde @@ -0,0 +1,90 @@ +/*This is an example of the EasyTransfer Library 2way communications. + +This sketch is for the Arduino with the servo attached to pin 9. + +The other Arduino has a potentiometer attached to analog pin 0. +Both have a putton attached to pin 12 and output a status using the LED on pin 13. + +The idea is each arduino will read the status of the button attached to it, and send it +to the other Arduino, which will toggle it's LED based on the others button. The button +should connect pin 12 to ground when pushed. + +And the Arduino with the potentiometer will send it's value to the one with the servo. +The servo will move to the position based on the potentiometer. +*/ + +#include +#include + +//create two objects +EasyTransfer ETin, ETout; +//create servo +Servo myservo; + +struct RECEIVE_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int buttonstate; + int servoval; +}; + +struct SEND_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int buttonstate; +}; + + +//give a name to the group of data +RECEIVE_DATA_STRUCTURE rxdata; +SEND_DATA_STRUCTURE txdata; + + +void setup(){ + Serial.begin(9600); + //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. + ETin.begin(details(rxdata), &Serial); + ETout.begin(details(txdata), &Serial); + + pinMode(13, OUTPUT); + pinMode(12, INPUT); + //enable pull-up + digitalWrite(12, HIGH); + + myservo.attach(9); +} + +void loop(){ + + //first, lets read our button and store it in our data structure + if(!digitalRead(12)) + txdata.buttonstate = HIGH; + else + txdata.buttonstate = LOW; + + //then we will go ahead and send that data out + ETout.sendData(); + + //there's a loop here so that we run the recieve function more often then the + //transmit function. This is important due to the slight differences in + //the clock speed of different Arduinos. If we didn't do this, messages + //would build up in the buffer and appear to cause a delay. + + for(int i=0; i<5; i++){ + //remember, you could use an if() here to check for new data, this time it's not needed. + ETin.receiveData(); + + //set our LED on or off based on what we received from the other Arduino + digitalWrite(13, rxdata.buttonstate); + + //set our servo position based on what we received from the other Arduino + //we will also map the ADC value to a servo value + myservo.write(map(rxdata.servoval, 0, 1023, 0, 179)); + + //delay + delay(10); + } + + //delay for good measure + delay(10); +} diff --git a/EasyTransfer/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/EasyTransfer/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde new file mode 100644 index 0000000..166e1cd --- /dev/null +++ b/EasyTransfer/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde @@ -0,0 +1,40 @@ +#include + +//create object +EasyTransfer ET; + +struct RECEIVE_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int blinks; + int pause; +}; + +//give a name to the group of data +RECEIVE_DATA_STRUCTURE mydata; + +void setup(){ + Serial.begin(9600); + //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. + ET.begin(details(mydata), &Serial); + + pinMode(13, OUTPUT); + +} + +void loop(){ + //check and see if a data packet has come in. + if(ET.receiveData()){ + //this is how you access the variables. [name of the group].[variable name] + //since we have data, we will blink it out. + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + } + + //you should make this delay shorter then your transmit delay or else messages could be lost + delay(250); +} diff --git a/EasyTransfer/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde b/EasyTransfer/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde new file mode 100644 index 0000000..96489a4 --- /dev/null +++ b/EasyTransfer/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde @@ -0,0 +1,43 @@ +#include + +//create object +EasyTransfer ET; + +struct SEND_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to send + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int blinks; + int pause; +}; + +//give a name to the group of data +SEND_DATA_STRUCTURE mydata; + +void setup(){ + Serial.begin(9600); + //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. + ET.begin(details(mydata), &Serial); + + pinMode(13, OUTPUT); + + randomSeed(analogRead(0)); + +} + +void loop(){ + //this is how you access the variables. [name of the group].[variable name] + mydata.blinks = random(5); + mydata.pause = random(5); + //send the data + ET.sendData(); + + //Just for fun, we will blink it out too + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + + delay(5000); +} diff --git a/EasyTransfer/keywords.txt b/EasyTransfer/keywords.txt new file mode 100644 index 0000000..5cf6417 --- /dev/null +++ b/EasyTransfer/keywords.txt @@ -0,0 +1,23 @@ +####################################### +# Syntax Coloring Map EasyTransfer +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +EasyTransfer KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +Button KEYWORD2 +sendData KEYWORD2 +receiveData KEYWORD2 +init KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### +details LITERAL1 diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..e69de29 From 5faabd30393280723ea58cd911ed88dd38eff37b Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Mon, 26 Sep 2011 20:08:43 -0500 Subject: [PATCH 02/67] Added readme txt Signed-off-by: Bill Porter --- README.txt | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.txt b/README.txt index e69de29..cecdfcc 100644 --- a/README.txt +++ b/README.txt @@ -0,0 +1,47 @@ +/****************************************************************** +* EasyTransfer Arduino Library v1.6 +* details and example sketch: +* http://www.billporter.info/easytransfer-arduino-library/ +* +* Brought to you by: +* Bill Porter +* www.billporter.info +* +* Lib version history +* 1.0 Created +* 1.1 Fixed dumb Copy-paste error in header file +* Added a keyword file +* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a better way +* added passing in of Serial port of different types +* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum failed, +* I'm good at dumb mistakes +* 1.7 Fixed a bug where the receive function could block for too long and never process data correctly +* Organized the examples to be Arduino IDE compatible +* +* +* +* Limits of the Library +* You can change the Serial port, +* but the Struct size must not pass 255 bytes +* +* The protcol is as follows: +* Header(0x06,0x85),SizeofPayload,Payload,Checksum +* +* +*This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ + + +Library now has two versions, one for regular hardware Serial, one for use with the NewSoftSerial library +making any Arduino pin capable of transfering data back and forth easily. + +See the examples to find out how to use the library. \ No newline at end of file From 34b7153a1338cb6ef8b2b19f34de4add3a781d1e Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Mon, 26 Sep 2011 20:30:06 -0500 Subject: [PATCH 03/67] Updated Readme Signed-off-by: Bill Porter --- README.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.txt b/README.txt index cecdfcc..b220499 100644 --- a/README.txt +++ b/README.txt @@ -41,6 +41,19 @@ GNU General Public License for more details. ******************************************************************/ +********************To Install************************************* + +To install, unzip and place 'EasyTransfer' folder into your 'C:\Users\{user name}\Documents\Arduino\libraries' folder or '{Arduino IDE path}\hardware\libraries" or {Arduino IDE path}\libraries" directory. + +Restart the Arduino IDE, look for the Library under "Sketch" -> "Import Library". You can also try the examples by finding them +under "File" -> "Examples" -> "EasyTransfer". + +All uses of the library are in the example sketchs. + + +******************************************************************* + + Library now has two versions, one for regular hardware Serial, one for use with the NewSoftSerial library making any Arduino pin capable of transfering data back and forth easily. From 98f4d8f307c1a4439956c340542e4adfd2201091 Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Mon, 26 Sep 2011 20:31:00 -0500 Subject: [PATCH 04/67] Signed-off-by: Bill Porter --- README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.txt b/README.txt index b220499..96fa7d7 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,5 @@ /****************************************************************** -* EasyTransfer Arduino Library v1.6 +* EasyTransfer Arduino Library v1.7 * details and example sketch: * http://www.billporter.info/easytransfer-arduino-library/ * From e990a38f4a27aa64c86e13895dda7637545b3299 Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Sat, 3 Dec 2011 20:10:05 -0600 Subject: [PATCH 05/67] Now Arduino 1.0 compatible! Signed-off-by: Bill Porter --- EasyTransfer/EasyTransfer.h | 4 ++++ README.txt | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/EasyTransfer/EasyTransfer.h b/EasyTransfer/EasyTransfer.h index be65717..48211b8 100644 --- a/EasyTransfer/EasyTransfer.h +++ b/EasyTransfer/EasyTransfer.h @@ -29,7 +29,11 @@ GNU General Public License for more details. #define details(name) (byte*)&name,sizeof(name) //Not neccessary, but just in case. +#if ARDUINO > 22 +#include "Arduino.h" +#else #include "WProgram.h" +#endif #include "HardwareSerial.h" //#include #include diff --git a/README.txt b/README.txt index 96fa7d7..ac89911 100644 --- a/README.txt +++ b/README.txt @@ -17,7 +17,8 @@ * I'm good at dumb mistakes * 1.7 Fixed a bug where the receive function could block for too long and never process data correctly * Organized the examples to be Arduino IDE compatible -* +* 1.8 +* Now Arduino 1.0 compatible! * * * Limits of the Library From 3457aa0223906e869884a4af0a636da419c5098d Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Sat, 3 Dec 2011 20:46:27 -0600 Subject: [PATCH 06/67] Arduino 1.0, take 2. Signed-off-by: Bill Porter --- EasyTransfer/EasyTransfer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/EasyTransfer/EasyTransfer.cpp b/EasyTransfer/EasyTransfer.cpp index dd84ab6..67b1f18 100644 --- a/EasyTransfer/EasyTransfer.cpp +++ b/EasyTransfer/EasyTransfer.cpp @@ -13,14 +13,14 @@ _serial = theSerial; //Sends out struct in binary, with header, length info and checksum void EasyTransfer::sendData(){ uint8_t CS = size; - _serial->print(0x06, BYTE); - _serial->print(0x85, BYTE); - _serial->print(size, BYTE); + _serial->write(0x06); + _serial->write(0x85); + _serial->write(size); for(int i = 0; iprint(*(address+i), BYTE); + _serial->write(*(address+i)); } - _serial->print(CS); + _serial->write(CS); } From a93b1e3553f4d6e2d543acef4a4e1f24244fe86d Mon Sep 17 00:00:00 2001 From: Mathieu Alorent Date: Sun, 11 Dec 2011 23:36:29 +0100 Subject: [PATCH 07/67] Forked EasyTransfer to EasyTransferI2C This library use exactly the same protocol as EasyTransfer, but communicate via I2C instead of Uarts. --- EasyTransferI2C/EasyTransferI2C.cpp | 85 +++++++++++++++++++ EasyTransferI2C/EasyTransferI2C.h | 65 ++++++++++++++ .../EasyTransfer_RX_Example.pde | 45 ++++++++++ .../EasyTransfer_TX_Example.pde | 47 ++++++++++ EasyTransferI2C/keywords.txt | 23 +++++ 5 files changed, 265 insertions(+) create mode 100644 EasyTransferI2C/EasyTransferI2C.cpp create mode 100644 EasyTransferI2C/EasyTransferI2C.h create mode 100644 EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde create mode 100644 EasyTransferI2C/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde create mode 100644 EasyTransferI2C/keywords.txt diff --git a/EasyTransferI2C/EasyTransferI2C.cpp b/EasyTransferI2C/EasyTransferI2C.cpp new file mode 100644 index 0000000..d532d45 --- /dev/null +++ b/EasyTransferI2C/EasyTransferI2C.cpp @@ -0,0 +1,85 @@ +#include "EasyTransferI2C.h" + + + + +//Captures address and size of struct +void EasyTransferI2C::begin(uint8_t * ptr, uint8_t length, TwoWire *theSerial){ +address = ptr; +size = length; +_serial = theSerial; +} + +//Sends out struct in binary, with header, length info and checksum +void EasyTransferI2C::sendData(uint8_t i2c_address){ + uint8_t CS = size; + _serial->beginTransmission(i2c_address); + _serial->send(0x06); + _serial->send(0x85); + _serial->send(size); + for(int i = 0; isend(*(address+i)); + } + _serial->send(CS); + _serial->endTransmission(); +} + +boolean EasyTransferI2C::receiveData(){ + + //start off by looking for the header bytes. If they were already found in a previous call, skip it. + if(rx_len == 0){ + //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. + if(_serial->available() >= 3){ + //this will block until a 0x06 is found or buffer size becomes less then 3. + while(_serial->receive() != 0x06) { + //This will trash any preamble junk in the serial buffer + //but we need to make sure there is enough in the buffer to process while we trash the rest + //if the buffer becomes too empty, we will escape and try again on the next call + if(_serial->available() < 3) + return false; + } + if (_serial->receive() == 0x85){ + rx_len = _serial->receive(); + //make sure the binary structs on both Arduinos are the same size. + if(rx_len != size){ + rx_len = 0; + return false; + } + } + } + } + + //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. + if(rx_len != 0){ + while(_serial->available() && rx_array_inx <= rx_len){ + rx_array[rx_array_inx++] = _serial->receive(); + } + + if(rx_len == (rx_array_inx-1)){ + //seem to have got whole message + //last uint8_t is CS + calc_CS = rx_len; + for (int i = 0; i +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ +#ifndef EasyTransferI2C_h +#define EasyTransferI2C_h + + +//make it a little prettier on the front end. +#define details(name) (byte*)&name,sizeof(name) + +//Not neccessary, but just in case. +#if ARDUINO > 22 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "HardwareSerial.h" +//#include +#include +#include +#include +#include +#include + +class EasyTransferI2C { +public: +void begin(uint8_t *, uint8_t, TwoWire *theSerial); +void sendData(uint8_t address); +boolean receiveData(); +private: +TwoWire *_serial; +//NewSoftSerial *_serial; +uint8_t * address; //address of struct +uint8_t size; //size of struct +uint8_t rx_len; //RX packet length according to the packet +uint8_t rx_array[255]; //RX packet parsing buffer +uint8_t rx_array_inx; //index for RX parsing buffer +uint8_t calc_CS; //calculated Chacksum +}; + + + +#endif diff --git a/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde new file mode 100644 index 0000000..afb6a0b --- /dev/null +++ b/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde @@ -0,0 +1,45 @@ +#include +#include + +//create object +EasyTransferI2C ET; + +struct RECEIVE_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int blinks; + int pause; +}; + +//give a name to the group of data +RECEIVE_DATA_STRUCTURE mydata; + +//define slave i2c address +#define I2C_SLAVE_ADDRESS 9 + +void setup(){ + Wire.begin(I2C_SLAVE_ADDRESS); + //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. + ET.begin(details(mydata), &Wire); + //define handler function on receiving data + Wire.onReceive(receive); + + pinMode(13, OUTPUT); + +} + +void loop() {} + +void receive(int numBytes){ + //check and see if a data packet has come in. + if(ET.receiveData()){ + //this is how you access the variables. [name of the group].[variable name] + //since we have data, we will blink it out. + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + } +} diff --git a/EasyTransferI2C/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde b/EasyTransferI2C/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde new file mode 100644 index 0000000..1422a99 --- /dev/null +++ b/EasyTransferI2C/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde @@ -0,0 +1,47 @@ +#include +#include + +//create object +EasyTransferI2C ET; + +struct SEND_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to send + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int blinks; + int pause; +}; + +//give a name to the group of data +SEND_DATA_STRUCTURE mydata; + +//define slave i2c address +#define I2C_SLAVE_ADDRESS 9 + +void setup(){ + Wire.begin(); + //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. + ET.begin(details(mydata), &Wire); + + pinMode(13, OUTPUT); + + randomSeed(analogRead(0)); + +} + +void loop(){ + //this is how you access the variables. [name of the group].[variable name] + mydata.blinks = random(5); + mydata.pause = random(5); + //send the data + ET.sendData(I2C_SLAVE_ADDRESS); + + //Just for fun, we will blink it out too + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + + delay(5000); +} diff --git a/EasyTransferI2C/keywords.txt b/EasyTransferI2C/keywords.txt new file mode 100644 index 0000000..f589ad5 --- /dev/null +++ b/EasyTransferI2C/keywords.txt @@ -0,0 +1,23 @@ +####################################### +# Syntax Coloring Map EasyTransfer +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +EasyTransferI2C KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +Button KEYWORD2 +sendData KEYWORD2 +receiveData KEYWORD2 +init KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### +details LITERAL1 From 984a378a170880829e9808fc93f13082428c3995 Mon Sep 17 00:00:00 2001 From: Mathieu Alorent Date: Sun, 25 Dec 2011 19:51:54 +0100 Subject: [PATCH 08/67] Make the example work ;) Thx coopermaa --- .../EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde index afb6a0b..bf8ec4f 100644 --- a/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde +++ b/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde @@ -28,9 +28,7 @@ void setup(){ } -void loop() {} - -void receive(int numBytes){ +void loop() { //check and see if a data packet has come in. if(ET.receiveData()){ //this is how you access the variables. [name of the group].[variable name] @@ -43,3 +41,5 @@ void receive(int numBytes){ } } } + +void receive(int numBytes) {} From f84a6e23cb43de53f1ed6284973f2d78bfbad48b Mon Sep 17 00:00:00 2001 From: Mathieu Alorent Date: Sun, 25 Dec 2011 20:01:56 +0100 Subject: [PATCH 09/67] Made it Arduino 1.0 compatible Thx to coopermaa --- EasyTransferI2C/EasyTransferI2C.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/EasyTransferI2C/EasyTransferI2C.cpp b/EasyTransferI2C/EasyTransferI2C.cpp index d532d45..085f9bf 100644 --- a/EasyTransferI2C/EasyTransferI2C.cpp +++ b/EasyTransferI2C/EasyTransferI2C.cpp @@ -14,14 +14,28 @@ _serial = theSerial; void EasyTransferI2C::sendData(uint8_t i2c_address){ uint8_t CS = size; _serial->beginTransmission(i2c_address); +#if ARDUINO >= 100 + _serial->write(0x06); + _serial->write(0x85); + _serial->write(size); +#else _serial->send(0x06); _serial->send(0x85); _serial->send(size); +#endif for(int i = 0; i= 100 + _serial->write(*(address+i)); +#else _serial->send(*(address+i)); +#endif } +#if ARDUINO >= 100 + _serial->write(CS); +#else _serial->send(CS); +#endif _serial->endTransmission(); } @@ -32,15 +46,24 @@ boolean EasyTransferI2C::receiveData(){ //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. if(_serial->available() >= 3){ //this will block until a 0x06 is found or buffer size becomes less then 3. +#if ARDUINO >= 100 + while(_serial->read() != 0x06) { +#else while(_serial->receive() != 0x06) { +#endif //This will trash any preamble junk in the serial buffer //but we need to make sure there is enough in the buffer to process while we trash the rest //if the buffer becomes too empty, we will escape and try again on the next call if(_serial->available() < 3) return false; } +#if ARDUINO >= 100 + if (_serial->read() == 0x85){ + rx_len = _serial->read(); +#else if (_serial->receive() == 0x85){ rx_len = _serial->receive(); +#endif //make sure the binary structs on both Arduinos are the same size. if(rx_len != size){ rx_len = 0; @@ -53,7 +76,11 @@ boolean EasyTransferI2C::receiveData(){ //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. if(rx_len != 0){ while(_serial->available() && rx_array_inx <= rx_len){ +#if ARDUINO >= 100 + rx_array[rx_array_inx++] = _serial->read(); +#else rx_array[rx_array_inx++] = _serial->receive(); +#endif } if(rx_len == (rx_array_inx-1)){ From 17e41cafb4ef75efa818d09887d9f16cae2a61a5 Mon Sep 17 00:00:00 2001 From: Mathieu Alorent Date: Sun, 25 Dec 2011 20:29:46 +0100 Subject: [PATCH 10/67] Add wiring exemple pictures. Images made with Fitzing. --- I2C_Wiring.png | Bin 0 -> 52872 bytes UARTS_Wiring.png | Bin 0 -> 44889 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 I2C_Wiring.png create mode 100644 UARTS_Wiring.png diff --git a/I2C_Wiring.png b/I2C_Wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..73e24a11898a73f076badba97e0e86d2434fc405 GIT binary patch literal 52872 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 a&+wz%+6y9VH_-VFfyhcKN|cHj1^zGFQx1#( literal 0 HcmV?d00001 diff --git a/UARTS_Wiring.png b/UARTS_Wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..48ba613a928ec1aa0b7f02093d56810078ae53c6 GIT binary patch literal 44889 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
@3@c~ZpF*CstMCQeZpME#4&M54U0;P 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 U6~Ll#LBO9fL__|QoJIKm0kF@_5&!@I literal 0 HcmV?d00001 From 493679cd66820cee129f3c9b549e975a73bee06d Mon Sep 17 00:00:00 2001 From: Mathieu Alorent Date: Sun, 25 Dec 2011 23:26:32 +0100 Subject: [PATCH 11/67] croped image --- I2C_Wiring.png | Bin 52872 -> 44906 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/I2C_Wiring.png b/I2C_Wiring.png index 73e24a11898a73f076badba97e0e86d2434fc405..4c15cd736d3ed5443768179aa453d63e40c84c18 100644 GIT binary patch literal 44906 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 a&+wz%+6y9VH_-VFfyhcKN|cHj1^zGFQx1#( From afd2bb38a63e1a40ab52aa81a5a1c5e9a6fb9698 Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Tue, 31 Jan 2012 20:23:56 -0600 Subject: [PATCH 12/67] 2.0 update, merged in Soft version, wrote Virtual Wire version, untested. Signed-off-by: Bill Porter --- EasyTransfer/I2C_Wiring.png | Bin 0 -> 44906 bytes EasyTransfer/README.txt | 61 ++++++++++ EasyTransfer/UARTS_Wiring.png | Bin 0 -> 44889 bytes EasyTransfer/keywords.txt | 3 +- EasyTransferI2C/keywords.txt | 3 +- .../EasyTransferVirtualWire.cpp | 71 +++++++++++ .../EasyTransferVirtualWire.h | 64 ++++++++++ .../ETVirtualWireDemoRX.pde | 51 ++++++++ .../ETVirtualWireDemoTX.pde | 50 ++++++++ EasyTransferVirtualWire/keywords.txt | 22 ++++ README.txt | 13 +- .../SoftEasyTransfer_RX_Example.pde | 48 ++++++++ .../SoftEasyTransfer_TX_Example.pde | 53 +++++++++ SoftEasyTransfer/SoftEasyTransfer.cpp | 112 ++++++++++++++++++ SoftEasyTransfer/SoftEasyTransfer.h | 77 ++++++++++++ SoftEasyTransfer/keywords.txt | 22 ++++ 16 files changed, 645 insertions(+), 5 deletions(-) create mode 100644 EasyTransfer/I2C_Wiring.png create mode 100644 EasyTransfer/README.txt create mode 100644 EasyTransfer/UARTS_Wiring.png create mode 100644 EasyTransferVirtualWire/EasyTransferVirtualWire.cpp create mode 100644 EasyTransferVirtualWire/EasyTransferVirtualWire.h create mode 100644 EasyTransferVirtualWire/Examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde create mode 100644 EasyTransferVirtualWire/Examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde create mode 100644 EasyTransferVirtualWire/keywords.txt create mode 100644 SoftEasyTransfer/Examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde create mode 100644 SoftEasyTransfer/Examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde create mode 100644 SoftEasyTransfer/SoftEasyTransfer.cpp create mode 100644 SoftEasyTransfer/SoftEasyTransfer.h create mode 100644 SoftEasyTransfer/keywords.txt diff --git a/EasyTransfer/I2C_Wiring.png b/EasyTransfer/I2C_Wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..4c15cd736d3ed5443768179aa453d63e40c84c18 GIT binary patch literal 44906 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 m8!O2GDg9&xL}~;3{y>zu4ArR)r)NR|e$ +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ + + +********************To Install************************************* + +To install, unzip and place 'EasyTransfer' folder into your 'C:\Users\{user name}\Documents\Arduino\libraries' folder or '{Arduino IDE path}\hardware\libraries" or {Arduino IDE path}\libraries" directory. + +Restart the Arduino IDE, look for the Library under "Sketch" -> "Import Library". You can also try the examples by finding them +under "File" -> "Examples" -> "EasyTransfer". + +All uses of the library are in the example sketchs. + + +******************************************************************* + + +Library now has two versions, one for regular hardware Serial, one for use with the NewSoftSerial library +making any Arduino pin capable of transfering data back and forth easily. + +See the examples to find out how to use the library. \ No newline at end of file diff --git a/EasyTransfer/UARTS_Wiring.png b/EasyTransfer/UARTS_Wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..48ba613a928ec1aa0b7f02093d56810078ae53c6 GIT binary patch literal 44889 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
@3@c~ZpF*CstMCQeZpME#4&M54U0;P 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 U6~Ll#LBO9fL__|QoJIKm0kF@_5&!@I literal 0 HcmV?d00001 diff --git a/EasyTransfer/keywords.txt b/EasyTransfer/keywords.txt index 5cf6417..d394efa 100644 --- a/EasyTransfer/keywords.txt +++ b/EasyTransfer/keywords.txt @@ -11,10 +11,9 @@ EasyTransfer KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -Button KEYWORD2 sendData KEYWORD2 receiveData KEYWORD2 -init KEYWORD2 +begin KEYWORD2 ####################################### diff --git a/EasyTransferI2C/keywords.txt b/EasyTransferI2C/keywords.txt index f589ad5..8e83157 100644 --- a/EasyTransferI2C/keywords.txt +++ b/EasyTransferI2C/keywords.txt @@ -11,10 +11,9 @@ EasyTransferI2C KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -Button KEYWORD2 sendData KEYWORD2 receiveData KEYWORD2 -init KEYWORD2 +begin KEYWORD2 ####################################### diff --git a/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp b/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp new file mode 100644 index 0000000..84aee06 --- /dev/null +++ b/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp @@ -0,0 +1,71 @@ +#include "EasyTransferVirtualWire.h" +#include + +//so to make this easy, I'm just making my library be another data +// layer on top of Virtual wire. + + +//Captures address and size of struct +void EasyTransferVirtualWire::begin(uint8_t * ptr, uint8_t length) { //HardwareSerial *theSerial){ +address = ptr; +size = length; +//_serial = theSerial; +} + +//Sends out struct in binary, with header, length info and checksum +void EasyTransferVirtualWire::sendData(){ + + //temp storage place + uint8_t temp_buffer[size+4]; + + uint8_t CS = size; + temp_buffer[0] = 0x06; + temp_buffer[1] = 0x85; + temp_buffer[2] = size; + + for(int i = 0; i +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ +#ifndef EasyTransferVirtualWire_h +#define EasyTransferVirtualWire_h + + +//make it a little prettier on the front end. +#define details(name) (byte*)&name,sizeof(name) + +//Not neccessary, but just in case. +#if ARDUINO > 22 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include +//#include "HardwareSerial.h" +//#include +#include +#include +#include +#include + +class EasyTransferVirtualWire { +public: +void begin(uint8_t *, uint8_t); +//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); +void sendData(); +boolean receiveData(); +private: +//HardwareSerial *_serial; +//NewSoftSerial *_serial; +uint8_t * address; //address of struct +uint8_t size; //size of struct +uint8_t rx_len; //RX packet length according to the packet +uint8_t rx_array[255]; //RX packet parsing buffer +uint8_t rx_array_inx; //index for RX parsing buffer +uint8_t calc_CS; //calculated Chacksum +}; + + + +#endif \ No newline at end of file diff --git a/EasyTransferVirtualWire/Examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde b/EasyTransferVirtualWire/Examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde new file mode 100644 index 0000000..eba62a9 --- /dev/null +++ b/EasyTransferVirtualWire/Examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde @@ -0,0 +1,51 @@ +#include +#include + + +//create object +EasyTransferVirtualWire ET; + +struct SEND_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to send + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + //Struct can'e be bigger then 26 bytes for VirtualWire version + int blinks; + int pause; +}; + +//give a name to the group of data +SEND_DATA_STRUCTURE mydata; + +void setup(){ + //start the library, pass in the data details + ET.begin(details(mydata)); + + // Initialise the IO and ISR + vw_set_ptt_inverted(true); // Required for DR3100 + vw_setup(2000); // Bits per sec + + vw_rx_start(); // Start the receiver PLL running + + pinMode(13, OUTPUT); + + randomSeed(analogRead(0)); + +} + +void loop(){ + //check and see if a data packet has come in. + if(ET.receiveData()){ + //this is how you access the variables. [name of the group].[variable name] + //since we have data, we will blink it out. + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + } + + //you should make this delay shorter then your transmit delay or else messages could be lost + delay(250); +} + diff --git a/EasyTransferVirtualWire/Examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde b/EasyTransferVirtualWire/Examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde new file mode 100644 index 0000000..d42f1ee --- /dev/null +++ b/EasyTransferVirtualWire/Examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde @@ -0,0 +1,50 @@ +#include +#include + + +//create object +EasyTransferVirtualWire ET; + +struct SEND_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to send + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + //Struct can'e be bigger then 26 bytes for VirtualWire version + int blinks; + int pause; +}; + +//give a name to the group of data +SEND_DATA_STRUCTURE mydata; + +void setup(){ + //start the library, pass in the data details + ET.begin(details(mydata)); + + // Initialise the IO and ISR + vw_set_ptt_inverted(true); // Required for DR3100 + vw_setup(2000); // Bits per sec + + pinMode(13, OUTPUT); + + randomSeed(analogRead(0)); + +} + +void loop(){ + //this is how you access the variables. [name of the group].[variable name] + mydata.blinks = random(5); + mydata.pause = random(5); + //send the data + ET.sendData(); + + //Just for fun, we will blink it out too + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + + delay(5000); +} + diff --git a/EasyTransferVirtualWire/keywords.txt b/EasyTransferVirtualWire/keywords.txt new file mode 100644 index 0000000..49c101b --- /dev/null +++ b/EasyTransferVirtualWire/keywords.txt @@ -0,0 +1,22 @@ +####################################### +# Syntax Coloring Map EasyTransfer +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +EasyTransferVirtualWire KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +sendData KEYWORD2 +receiveData KEYWORD2 +begin KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### +details LITERAL1 diff --git a/README.txt b/README.txt index ac89911..8b594fa 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,5 @@ /****************************************************************** -* EasyTransfer Arduino Library v1.7 +* EasyTransfer Arduino Library v2.0 * details and example sketch: * http://www.billporter.info/easytransfer-arduino-library/ * @@ -7,6 +7,10 @@ * Bill Porter * www.billporter.info * +* Major props to Mathieu Alorent (kumy) for +* I2C version and the pretty pictures. +* +* * Lib version history * 1.0 Created * 1.1 Fixed dumb Copy-paste error in header file @@ -19,11 +23,18 @@ * Organized the examples to be Arduino IDE compatible * 1.8 * Now Arduino 1.0 compatible! +* 1.81 +* Made it more cross compatible. Man, They really made us work for this one. +* 2.0 +* Combined SoftEasyTransfer with the other two to make everything one repo +* Added EasyTransferVirtualWire library for use with Virtual Wire and cheap radios. +* UNTESTED! * * * Limits of the Library * You can change the Serial port, * but the Struct size must not pass 255 bytes +* VirtualWire Version Struct can'e be bigger then 26 bytes * * The protcol is as follows: * Header(0x06,0x85),SizeofPayload,Payload,Checksum diff --git a/SoftEasyTransfer/Examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde b/SoftEasyTransfer/Examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde new file mode 100644 index 0000000..70edbe2 --- /dev/null +++ b/SoftEasyTransfer/Examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde @@ -0,0 +1,48 @@ +#include + +/* For Arduino 1.0 and newer, do this: */ +#include +SoftwareSerial mySerial(2, 3); + +/* For Arduino 22 and older, do this: */ +//#include +//NewSoftSerial mySerial(2, 3); + + +//create object +SoftEasyTransfer ET; + +struct RECEIVE_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int blinks; + int pause; +}; + +//give a name to the group of data +RECEIVE_DATA_STRUCTURE mydata; + +void setup(){ + mySerial.begin(9600); + //start the library, pass in the data details and the name of the serial port. + ET.begin(details(mydata), &mySerial); + + pinMode(13, OUTPUT); + +} + +void loop(){ + //check and see if a data packet has come in. + if(ET.receiveData()){ + //this is how you access the variables. [name of the group].[variable name] + //since we have data, we will blink it out. + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + } + //you should make this delay shorter then your transmit delay or else messages could be lost + delay(250); +} diff --git a/SoftEasyTransfer/Examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde b/SoftEasyTransfer/Examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde new file mode 100644 index 0000000..07aecff --- /dev/null +++ b/SoftEasyTransfer/Examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde @@ -0,0 +1,53 @@ +#include + +/* For Arduino 1.0 and newer, do this: */ +#include +SoftwareSerial mySerial(2, 3); + +/* For Arduino 22 and older, do this: */ +//#include +//NewSoftSerial mySerial(2, 3); + + + +//create object +SoftEasyTransfer ET; + +struct SEND_DATA_STRUCTURE{ + //put your variable definitions here for the data you want to send + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int blinks; + int pause; +}; + +//give a name to the group of data +SEND_DATA_STRUCTURE mydata; + +void setup(){ + mySerial.begin(9600); + //start the library, pass in the data details and the name of the serial port. + ET.begin(details(mydata), &mySerial); + + pinMode(13, OUTPUT); + + randomSeed(analogRead(0)); + +} + +void loop(){ + //this is how you access the variables. [name of the group].[variable name] + mydata.blinks = random(5); + mydata.pause = random(5); + //send the data + ET.sendData(); + + //Just for fun, we will blink it out too + for(int i = mydata.blinks; i>0; i--){ + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + + delay(5000); +} diff --git a/SoftEasyTransfer/SoftEasyTransfer.cpp b/SoftEasyTransfer/SoftEasyTransfer.cpp new file mode 100644 index 0000000..482f50a --- /dev/null +++ b/SoftEasyTransfer/SoftEasyTransfer.cpp @@ -0,0 +1,112 @@ +#include "SoftEasyTransfer.h" + + + + +#if ARDUINO > 22 +//Captures address and size of struct +void SoftEasyTransfer::begin(uint8_t * ptr, uint8_t length, SoftwareSerial *theSerial){ +address = ptr; +size = length; +_serial = theSerial; +} + +#else +//Captures address and size of struct +void SoftEasyTransfer::begin(uint8_t * ptr, uint8_t length, NewSoftSerial *theSerial){ +address = ptr; +size = length; +_serial = theSerial; +} + +#endif + +#if ARDUINO > 22 +//Sends out struct in binary, with header, length info and checksum +void SoftEasyTransfer::sendData(){ + uint8_t CS = size; + _serial->write(0x06); + _serial->write(0x85); + _serial->write(size); + for(int i = 0; iwrite(*(address+i)); + } + _serial->write(CS); + +} +#else +//Sends out struct in binary, with header, length info and checksum +void SoftEasyTransfer::sendData(){ + uint8_t CS = size; + _serial->print(0x06, BYTE); + _serial->print(0x85, BYTE); + _serial->print(size, BYTE); + for(int i = 0; iprint(*(address+i), BYTE); + } + _serial->print(CS, BYTE); + +} +#endif + +boolean SoftEasyTransfer::receiveData(){ + + //start off by looking for the header bytes. If they were already found in a previous call, skip it. + if(rx_len == 0){ + //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. + if(_serial->available() >= 3){ + //this will block until a 0x06 is found or buffer size becomes less then 3. + while(_serial->read() != 0x06) { + //This will trash any preamble junk in the serial buffer + //but we need to make sure there is enough in the buffer to process while we trash the rest + //if the buffer becomes too empty, we will escape and try again on the next call + if(_serial->available() < 3) + return false; + } + if (_serial->read() == 0x85){ + rx_len = _serial->read(); + //make sure the binary structs on both Arduinos are the same size. + if(rx_len != size){ + rx_len = 0; + return false; + } + } + } + } + + + + if(rx_len != 0){ + while(_serial->available() && rx_array_inx <= rx_len){ + rx_array[rx_array_inx++] = _serial->read(); + } + + if(rx_len == (rx_array_inx-1)){ + //seem to have got whole message + //last uint8_t is CS + calc_CS = rx_len; + for (int i = 0; i +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ +#ifndef SoftEasyTransfer_h +#define SoftEasyTransfer_h + + +//make it a little prettier on the front end. +#define details(name) (byte*)&name,sizeof(name) + +//Not neccessary, but just in case. +#if ARDUINO > 22 +#include "Arduino.h" +#include +#else +#include "WProgram.h" +#include +#endif +//#include "HardwareSerial.h" + + +#include +#include +#include +#include + +class SoftEasyTransfer { +public: +//void begin(uint8_t *, uint8_t, HardwareSerial *theSerial); +#if ARDUINO > 22 +void begin(uint8_t *, uint8_t, SoftwareSerial *theSerial); +#else +void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); +#endif +void sendData(); +boolean receiveData(); +private: +//HardwareSerial *_serial; + +#if ARDUINO > 22 +SoftwareSerial *_serial; +#else +NewSoftSerial *_serial; +#endif + + +uint8_t * address; //address of struct +uint8_t size; //size of struct +uint8_t rx_len; //RX packet length according to the packet +uint8_t rx_array[255]; //RX packet parsing buffer +uint8_t rx_array_inx; //index for RX parsing buffer +uint8_t calc_CS; //calculated Chacksum +}; + + + +#endif \ No newline at end of file diff --git a/SoftEasyTransfer/keywords.txt b/SoftEasyTransfer/keywords.txt new file mode 100644 index 0000000..3b2ecb4 --- /dev/null +++ b/SoftEasyTransfer/keywords.txt @@ -0,0 +1,22 @@ +####################################### +# Syntax Coloring Map EasyTransfer +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SoftEasyTransfer KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +sendData KEYWORD2 +receiveData KEYWORD2 +begin KEYWORD2 + + +####################################### +# Constants (LITERAL1) +####################################### +details LITERAL1 From 3ab8e9a9be05b165f97b3fdc1c2cd2eb72aeebaf Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Wed, 1 Feb 2012 07:15:42 -0600 Subject: [PATCH 13/67] VirtualWire version tested, bugs fixed. Signed-off-by: Bill Porter --- EasyTransferVirtualWire/EasyTransferVirtualWire.cpp | 5 ++++- README.txt | 11 ++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp b/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp index 84aee06..e56a941 100644 --- a/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp +++ b/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp @@ -46,6 +46,9 @@ boolean EasyTransferVirtualWire::receiveData(){ if (buf[0] == 0x06 && buf[1] == 0x85 && buf[2] == size) { //found my headers, size matches + + calc_CS = buf[2]; // must be added after the header is received + for(int i = 0; i Date: Sat, 11 Feb 2012 13:41:48 -0600 Subject: [PATCH 14/67] Changed RX buffer to dynamic memory allocation to save RAM Signed-off-by: Bill Porter --- EasyTransfer/EasyTransfer.cpp | 17 ++++++++++------- EasyTransfer/EasyTransfer.h | 4 ++-- EasyTransferI2C/EasyTransferI2C.cpp | 19 +++++++++++-------- EasyTransferI2C/EasyTransferI2C.h | 4 ++-- README.txt | 4 +++- SoftEasyTransfer/SoftEasyTransfer.cpp | 26 ++++++++++++++++---------- SoftEasyTransfer/SoftEasyTransfer.h | 4 ++-- 7 files changed, 46 insertions(+), 32 deletions(-) diff --git a/EasyTransfer/EasyTransfer.cpp b/EasyTransfer/EasyTransfer.cpp index 67b1f18..4d9d672 100644 --- a/EasyTransfer/EasyTransfer.cpp +++ b/EasyTransfer/EasyTransfer.cpp @@ -5,9 +5,12 @@ //Captures address and size of struct void EasyTransfer::begin(uint8_t * ptr, uint8_t length, HardwareSerial *theSerial){ -address = ptr; -size = length; -_serial = theSerial; + address = ptr; + size = length; + _serial = theSerial; + + //dynamic creation of rx parsing buffer in RAM + rx_buffer = (uint8_t*) malloc(size); } //Sends out struct in binary, with header, length info and checksum @@ -52,7 +55,7 @@ boolean EasyTransfer::receiveData(){ //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. if(rx_len != 0){ while(_serial->available() && rx_array_inx <= rx_len){ - rx_array[rx_array_inx++] = _serial->read(); + rx_buffer[rx_array_inx++] = _serial->read(); } if(rx_len == (rx_array_inx-1)){ @@ -60,11 +63,11 @@ boolean EasyTransfer::receiveData(){ //last uint8_t is CS calc_CS = rx_len; for (int i = 0; iavailable() && rx_array_inx <= rx_len){ #if ARDUINO >= 100 - rx_array[rx_array_inx++] = _serial->read(); + rx_buffer[rx_array_inx++] = _serial->read(); #else - rx_array[rx_array_inx++] = _serial->receive(); + rx_buffer[rx_array_inx++] = _serial->receive(); #endif } @@ -88,11 +91,11 @@ boolean EasyTransferI2C::receiveData(){ //last uint8_t is CS calc_CS = rx_len; for (int i = 0; i 22 //Captures address and size of struct void SoftEasyTransfer::begin(uint8_t * ptr, uint8_t length, SoftwareSerial *theSerial){ -address = ptr; -size = length; -_serial = theSerial; + address = ptr; + size = length; + _serial = theSerial; + + //dynamic creation of rx parsing buffer in RAM + rx_buffer = (uint8_t*) malloc(size); } #else //Captures address and size of struct void SoftEasyTransfer::begin(uint8_t * ptr, uint8_t length, NewSoftSerial *theSerial){ -address = ptr; -size = length; -_serial = theSerial; + address = ptr; + size = length; + _serial = theSerial; + + //dynamic creation of rx parsing buffer in RAM + rx_buffer = (uint8_t*) malloc(size); } #endif @@ -80,7 +86,7 @@ boolean SoftEasyTransfer::receiveData(){ if(rx_len != 0){ while(_serial->available() && rx_array_inx <= rx_len){ - rx_array[rx_array_inx++] = _serial->read(); + rx_buffer[rx_array_inx++] = _serial->read(); } if(rx_len == (rx_array_inx-1)){ @@ -88,11 +94,11 @@ boolean SoftEasyTransfer::receiveData(){ //last uint8_t is CS calc_CS = rx_len; for (int i = 0; i Date: Wed, 15 May 2013 10:47:17 +0200 Subject: [PATCH 15/67] Added Arduino Robot libraries --- ArduinoRobotMotorBoard.cpp | 265 ++++++++++++++++++ ArduinoRobotMotorBoard.h | 125 +++++++++ EasyTransfer2.cpp | 152 ++++++++++ EasyTransfer2.h | 76 +++++ LineFollow.h | 40 +++ Multiplexer.cpp | 37 +++ Multiplexer.h | 24 ++ .../Robot_IR_Array_Test.ino | 26 ++ .../Robot_Motor_Core/Robot_Motor_Core.ino | 18 ++ lineFollow.cpp | 152 ++++++++++ 10 files changed, 915 insertions(+) create mode 100644 ArduinoRobotMotorBoard.cpp create mode 100644 ArduinoRobotMotorBoard.h create mode 100644 EasyTransfer2.cpp create mode 100644 EasyTransfer2.h create mode 100644 LineFollow.h create mode 100644 Multiplexer.cpp create mode 100644 Multiplexer.h create mode 100644 examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino create mode 100644 examples/Robot_Motor_Core/Robot_Motor_Core.ino create mode 100644 lineFollow.cpp diff --git a/ArduinoRobotMotorBoard.cpp b/ArduinoRobotMotorBoard.cpp new file mode 100644 index 0000000..7740a06 --- /dev/null +++ b/ArduinoRobotMotorBoard.cpp @@ -0,0 +1,265 @@ +#include "ArduinoRobotMotorBoard.h" +#include "EasyTransfer2.h" +#include "Multiplexer.h" +#include "LineFollow.h" + +RobotMotorBoard::RobotMotorBoard(){ + //LineFollow::LineFollow(); +} +/*void RobotMotorBoard::beginIRReceiver(){ + IRrecv::enableIRIn(); +}*/ +void RobotMotorBoard::begin(){ + //initialze communication + Serial1.begin(9600); + messageIn.begin(&Serial1); + messageOut.begin(&Serial1); + + //init MUX + uint8_t MuxPins[]={MUXA,MUXB,MUXC}; + this->IRs.begin(MuxPins,MUX_IN,3); + pinMode(MUXI,INPUT); + digitalWrite(MUXI,LOW); + + isPaused=false; +} + +void RobotMotorBoard::process(){ + if(isPaused)return;//skip process if the mode is paused + + if(mode==MODE_SIMPLE){ + //Serial.println("s"); + //do nothing? Simple mode is just about getting commands + }else if(mode==MODE_LINE_FOLLOW){ + //do line following stuff here. + LineFollow::runLineFollow(); + }else if(mode==MODE_ADJUST_MOTOR){ + //Serial.println('a'); + //motorAdjustment=analogRead(POT); + //setSpeed(255,255); + //delay(100); + } +} +void RobotMotorBoard::pauseMode(bool onOff){ + if(onOff){ + isPaused=true; + }else{ + isPaused=false; + } + stopCurrentActions(); + +} +void RobotMotorBoard::parseCommand(){ + uint8_t modeName; + uint8_t codename; + int value; + int speedL; + int speedR; + if(this->messageIn.receiveData()){ + //Serial.println("data received"); + uint8_t command=messageIn.readByte(); + //Serial.println(command); + switch(command){ + case COMMAND_SWITCH_MODE: + modeName=messageIn.readByte(); + setMode(modeName); + break; + case COMMAND_RUN: + if(mode==MODE_LINE_FOLLOW)break;//in follow line mode, the motor does not follow commands + speedL=messageIn.readInt(); + speedR=messageIn.readInt(); + motorsWrite(speedL,speedR); + break; + case COMMAND_MOTORS_STOP: + motorsStop(); + break; + case COMMAND_ANALOG_WRITE: + codename=messageIn.readByte(); + value=messageIn.readInt(); + _analogWrite(codename,value); + break; + case COMMAND_DIGITAL_WRITE: + codename=messageIn.readByte(); + value=messageIn.readByte(); + _digitalWrite(codename,value); + break; + case COMMAND_ANALOG_READ: + codename=messageIn.readByte(); + _analogRead(codename); + break; + case COMMAND_DIGITAL_READ: + codename=messageIn.readByte(); + _digitalRead(codename); + break; + case COMMAND_READ_IR: + _readIR(); + break; + case COMMAND_READ_TRIM: + _readTrim(); + break; + case COMMAND_PAUSE_MODE: + pauseMode(messageIn.readByte());//onOff state + break; + case COMMAND_LINE_FOLLOW_CONFIG: + LineFollow::config( + messageIn.readByte(), //KP + messageIn.readByte(), //KD + messageIn.readByte(), //robotSpeed + messageIn.readByte() //IntegrationTime + ); + break; + } + } + //delay(5); +} +uint8_t RobotMotorBoard::parseCodename(uint8_t codename){ + switch(codename){ + case B_TK1: + return TK1; + case B_TK2: + return TK2; + case B_TK3: + return TK3; + case B_TK4: + return TK4; + } +} +uint8_t RobotMotorBoard::codenameToAPin(uint8_t codename){ + switch(codename){ + case B_TK1: + return A0; + case B_TK2: + return A1; + case B_TK3: + return A6; + case B_TK4: + return A11; + } +} + +void RobotMotorBoard::setMode(uint8_t mode){ + if(mode==MODE_LINE_FOLLOW){ + LineFollow::calibIRs(); + } + /*if(mode==SET_MOTOR_ADJUSTMENT){ + save_motor_adjustment_to_EEPROM(); + } + */ + /*if(mode==MODE_IR_CONTROL){ + beginIRReceiver(); + }*/ + this->mode=mode; + //stopCurrentActions();//If line following, this should stop the motors +} + +void RobotMotorBoard::stopCurrentActions(){ + motorsStop(); + //motorsWrite(0,0); +} + +void RobotMotorBoard::motorsWrite(int speedL, int speedR){ + /*Serial.print(speedL); + Serial.print(" "); + Serial.println(speedR);*/ + //motor adjustment, using percentage + _refreshMotorAdjustment(); + + if(motorAdjustment<0){ + speedR*=(1+motorAdjustment); + }else{ + speedL*=(1-motorAdjustment); + } + + if(speedL>0){ + analogWrite(IN_A1,speedL); + analogWrite(IN_A2,0); + }else{ + analogWrite(IN_A1,0); + analogWrite(IN_A2,-speedL); + } + + if(speedR>0){ + analogWrite(IN_B1,speedR); + analogWrite(IN_B2,0); + }else{ + analogWrite(IN_B1,0); + analogWrite(IN_B2,-speedR); + } +} +void RobotMotorBoard::motorsWritePct(int speedLpct, int speedRpct){ + //speedLpct, speedRpct ranges from -100 to 100 + motorsWrite(speedLpct*2.55,speedRpct*2.55); +} +void RobotMotorBoard::motorsStop(){ + analogWrite(IN_A1,255); + analogWrite(IN_A2,255); + + analogWrite(IN_B1,255); + analogWrite(IN_B2,255); +} + + +/* +* +* +* Input and Output ports +* +* +*/ +void RobotMotorBoard::_digitalWrite(uint8_t codename,bool value){ + uint8_t pin=parseCodename(codename); + digitalWrite(pin,value); +} +void RobotMotorBoard::_analogWrite(uint8_t codename,int value){ + //There's no PWM available on motor board +} +void RobotMotorBoard::_digitalRead(uint8_t codename){ + uint8_t pin=parseCodename(codename); + bool value=digitalRead(pin); + messageOut.writeByte(COMMAND_DIGITAL_READ_RE); + messageOut.writeByte(codename); + messageOut.writeByte(value); + messageOut.sendData(); +} +void RobotMotorBoard::_analogRead(uint8_t codename){ + uint8_t pin=codenameToAPin(codename); + int value=analogRead(pin); + messageOut.writeByte(COMMAND_ANALOG_READ_RE); + messageOut.writeByte(codename); + messageOut.writeInt(value); + messageOut.sendData(); +} +int RobotMotorBoard::IRread(uint8_t num){ + IRs.selectPin(num-1); //To make consistant with the pins labeled on the board + return IRs.getAnalogValue(); +} + +void RobotMotorBoard::_readIR(){ + //Serial.println("readIR"); + int value; + messageOut.writeByte(COMMAND_READ_IR_RE); + for(int i=1;i<6;i++){ + value=IRread(i); + messageOut.writeInt(value); + } + messageOut.sendData(); +} + +void RobotMotorBoard::_readTrim(){ + int value=analogRead(TRIM); + messageOut.writeByte(COMMAND_READ_TRIM_RE); + messageOut.writeInt(value); + messageOut.sendData(); +} + +void RobotMotorBoard::_refreshMotorAdjustment(){ + motorAdjustment=map(analogRead(TRIM),0,1023,-30,30)/100.0; +} + +void RobotMotorBoard::reportActionDone(){ + setMode(MODE_SIMPLE); + messageOut.writeByte(COMMAND_ACTION_DONE); + messageOut.sendData(); +} + +RobotMotorBoard RobotMotor=RobotMotorBoard(); \ No newline at end of file diff --git a/ArduinoRobotMotorBoard.h b/ArduinoRobotMotorBoard.h new file mode 100644 index 0000000..c1004c4 --- /dev/null +++ b/ArduinoRobotMotorBoard.h @@ -0,0 +1,125 @@ +#ifndef ArduinoRobot_h +#define ArduinoRobot_h + +#include "EasyTransfer2.h" +#include "Multiplexer.h" +#include "LineFollow.h" +//#include "IRremote.h" + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +//Command code +#define COMMAND_SWITCH_MODE 0 +#define COMMAND_RUN 10 +#define COMMAND_MOTORS_STOP 11 +#define COMMAND_ANALOG_WRITE 20 +#define COMMAND_DIGITAL_WRITE 30 +#define COMMAND_ANALOG_READ 40 +#define COMMAND_ANALOG_READ_RE 41 +#define COMMAND_DIGITAL_READ 50 +#define COMMAND_DIGITAL_READ_RE 51 +#define COMMAND_READ_IR 60 +#define COMMAND_READ_IR_RE 61 +#define COMMAND_ACTION_DONE 70 +#define COMMAND_READ_TRIM 80 +#define COMMAND_READ_TRIM_RE 81 +#define COMMAND_PAUSE_MODE 90 +#define COMMAND_LINE_FOLLOW_CONFIG 100 + + +//component codename +#define CN_LEFT_MOTOR 0 +#define CN_RIGHT_MOTOR 1 +#define CN_IR 2 + +//motor board modes +#define MODE_SIMPLE 0 +#define MODE_LINE_FOLLOW 1 +#define MODE_ADJUST_MOTOR 2 +#define MODE_IR_CONTROL 3 + +//bottom TKs, just for communication purpose +#define B_TK1 201 +#define B_TK2 202 +#define B_TK3 203 +#define B_TK4 204 + +/* +A message structure will be: +switch mode (2): + byte COMMAND_SWITCH_MODE, byte mode +run (5): + byte COMMAND_RUN, int speedL, int speedR +analogWrite (3): + byte COMMAND_ANALOG_WRITE, byte codename, byte value; +digitalWrite (3): + byte COMMAND_DIGITAL_WRITE, byte codename, byte value; +analogRead (2): + byte COMMAND_ANALOG_READ, byte codename; +analogRead _return_ (4): + byte COMMAND_ANALOG_READ_RE, byte codename, int value; +digitalRead (2): + byte COMMAND_DIGITAL_READ, byte codename; +digitalRead _return_ (4): + byte COMMAND_DIGITAL_READ_RE, byte codename, int value; +read IR (1): + byte COMMAND_READ_IR; +read IR _return_ (9): + byte COMMAND_READ_IR_RE, int valueA, int valueB, int valueC, int valueD; + + +*/ + +class RobotMotorBoard:public LineFollow{ + public: + RobotMotorBoard(); + void begin(); + + void process(); + + void parseCommand(); + + int IRread(uint8_t num); + + void setMode(uint8_t mode); + void pauseMode(bool onOff); + + void motorsWrite(int speedL, int speedR); + void motorsWritePct(int speedLpct, int speedRpct);//write motor values in percentage + void motorsStop(); + private: + float motorAdjustment;//-1.0 ~ 1.0, whether left is lowered or right is lowered + + //convert codename to actual pins + uint8_t parseCodename(uint8_t codename); + uint8_t codenameToAPin(uint8_t codename); + + void stopCurrentActions(); + //void sendCommand(byte command,byte codename,int value); + + void _analogWrite(uint8_t codename, int value); + void _digitalWrite(uint8_t codename, bool value); + void _analogRead(uint8_t codename); + void _digitalRead(uint8_t codename); + void _readIR(); + void _readTrim(); + + void _refreshMotorAdjustment(); + + Multiplexer IRs; + uint8_t mode; + uint8_t isPaused; + EasyTransfer2 messageIn; + EasyTransfer2 messageOut; + + //Line Following + void reportActionDone(); +}; + +extern RobotMotorBoard RobotMotor; + +#endif \ No newline at end of file diff --git a/EasyTransfer2.cpp b/EasyTransfer2.cpp new file mode 100644 index 0000000..24427cc --- /dev/null +++ b/EasyTransfer2.cpp @@ -0,0 +1,152 @@ +#include "EasyTransfer2.h" + + + + +//Captures address and size of struct +void EasyTransfer2::begin(HardwareSerial *theSerial){ + _serial = theSerial; + + //dynamic creation of rx parsing buffer in RAM + //rx_buffer = (uint8_t*) malloc(size); + + resetData(); +} + +void EasyTransfer2::writeByte(uint8_t dat){ + if(position<20) + data[position++]=dat; + size++; +} +void EasyTransfer2::writeInt(int dat){ + if(position<19){ + data[position++]=dat>>8; + data[position++]=dat; + size+=2; + } +} +uint8_t EasyTransfer2::readByte(){ + if(position>=size)return 0; + return data[position++]; +} +int EasyTransfer2::readInt(){ + if(position+1>=size)return 0; + int dat_1=data[position++]<<8; + int dat_2=data[position++]; + int dat= dat_1 | dat_2; + return dat; +} + +void EasyTransfer2::resetData(){ + for(int i=0;i<20;i++){ + data[i]=0; + } + size=0; + position=0; +} + +//Sends out struct in binary, with header, length info and checksum +void EasyTransfer2::sendData(){ + uint8_t CS = size; + _serial->write(0x06); + _serial->write(0x85); + _serial->write(size); + for(int i = 0; iwrite(*(data+i)); + //Serial.print(*(data+i)); + //Serial.print(","); + } + //Serial.println(""); + _serial->write(CS); + + resetData(); +} + +boolean EasyTransfer2::receiveData(){ + + //start off by looking for the header bytes. If they were already found in a previous call, skip it. + if(rx_len == 0){ + //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. + if(_serial->available() >= 3){ + //this will block until a 0x06 is found or buffer size becomes less then 3. + while(_serial->read() != 0x06) { + //This will trash any preamble junk in the serial buffer + //but we need to make sure there is enough in the buffer to process while we trash the rest + //if the buffer becomes too empty, we will escape and try again on the next call + if(_serial->available() < 3) + return false; + } + //Serial.println("head"); + if (_serial->read() == 0x85){ + rx_len = _serial->read(); + //Serial.print("rx_len:"); + //Serial.println(rx_len); + resetData(); + + //make sure the binary structs on both Arduinos are the same size. + /*if(rx_len != size){ + rx_len = 0; + return false; + }*/ + } + } + //Serial.println("nothing"); + } + + //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. + if(rx_len != 0){ + + while(_serial->available() && rx_array_inx <= rx_len){ + data[rx_array_inx++] = _serial->read(); + } + + if(rx_len == (rx_array_inx-1)){ + //seem to have got whole message + //last uint8_t is CS + calc_CS = rx_len; + //Serial.print("len:"); + //Serial.println(rx_len); + for (int i = 0; i +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ +#ifndef EasyTransfer2_h +#define EasyTransfer2_h + + +//make it a little prettier on the front end. +#define details(name) (byte*)&name,sizeof(name) + +//Not neccessary, but just in case. +#if ARDUINO > 22 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "HardwareSerial.h" +//#include +#include +#include +#include +#include + +class EasyTransfer2 { +public: +void begin(HardwareSerial *theSerial); +//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); +void sendData(); +boolean receiveData(); + +void writeByte(uint8_t dat); +void writeInt(int dat); +uint8_t readByte(); +int readInt(); + + +private: +HardwareSerial *_serial; + +void resetData(); + +uint8_t data[20]; //data storage, for both read and send +uint8_t position; +uint8_t size; //size of data in bytes. Both for read and send +//uint8_t * address; //address of struct +//uint8_t size; //size of struct +//uint8_t * rx_buffer; //address for temporary storage and parsing buffer +//uint8_t rx_buffer[20]; +uint8_t rx_array_inx; //index for RX parsing buffer +uint8_t rx_len; //RX packet length according to the packet +uint8_t calc_CS; //calculated Chacksum +}; + + + +#endif \ No newline at end of file diff --git a/LineFollow.h b/LineFollow.h new file mode 100644 index 0000000..608d573 --- /dev/null +++ b/LineFollow.h @@ -0,0 +1,40 @@ +#ifndef LINE_FOLLOW_H +#define LINE_FOLLOW_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +class LineFollow{ + public: + LineFollow(); + + void calibIRs(); + void runLineFollow(); + void config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime); + + //These are all pure virtual functions, pure VF needs pure specifier "=0" + //virtual void motorsWrite(int speedL, int speedR)=0; + virtual void motorsWritePct(int speedLpct, int speedRpct)=0; + virtual void motorsStop()=0; + virtual int IRread(uint8_t num)=0; + protected: + virtual void reportActionDone()=0; + + private: + void doCalibration(int speedPct, int time); + void ajusta_niveles(); + + uint8_t KP; + uint8_t KD; + uint8_t robotSpeed; //percentage + uint8_t intergrationTime; + + int lectura_sensor[5], last_error, acu; + int sensor_blanco[5]; + int sensor_negro[5]; +}; + +#endif \ No newline at end of file diff --git a/Multiplexer.cpp b/Multiplexer.cpp new file mode 100644 index 0000000..c0fdd86 --- /dev/null +++ b/Multiplexer.cpp @@ -0,0 +1,37 @@ +#include "Multiplexer.h" + +void Multiplexer::begin(uint8_t* selectors, uint8_t Z, uint8_t length){ + for(uint8_t i=0;iselectors[i]=selectors[i]; + pinMode(selectors[i],OUTPUT); + } + this->length=length; + this->pin_Z=Z; + pinMode(pin_Z,INPUT); +} + +void Multiplexer::selectPin(uint8_t num){ + for(uint8_t i=0;i= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +class Multiplexer{ + public: + void begin(uint8_t* selectors, uint8_t Z, uint8_t length); + void selectPin(uint8_t num); + int getAnalogValue(); + int getAnalogValueAt(uint8_t num); + bool getDigitalValue(); + bool getDigitalValueAt(uint8_t num); + private: + uint8_t selectors[4]; + uint8_t pin_Z; + uint8_t length; +}; + +#endif diff --git a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino new file mode 100644 index 0000000..e201fd9 --- /dev/null +++ b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino @@ -0,0 +1,26 @@ +/* Motor Board IR Array Test + + This example of the Arduno robot's motor board returns the + values read fron the 5 infrared sendors on the bottom of + the robot. + +*/ +// include the motor board header +#include + +String bar; // string for storing the informaton + +void setup(){ + // start serial communication + Serial.begin(9600); + // initialize the library + RobotMotor.begin(); +} +void loop(){ + bar=String(""); // empty the string + // read the sensors and add them to the string + bar=bar+RobotMotor.readIR(0)+' '+RobotMotor.readIR(1)+' '+RobotMotor.readIR(2)+' '+RobotMotor.readIR(3)+' '+RobotMotor.readIR(4); + // print out the values + Serial.println(bar); + delay(100); +} diff --git a/examples/Robot_Motor_Core/Robot_Motor_Core.ino b/examples/Robot_Motor_Core/Robot_Motor_Core.ino new file mode 100644 index 0000000..f74f30a --- /dev/null +++ b/examples/Robot_Motor_Core/Robot_Motor_Core.ino @@ -0,0 +1,18 @@ +/* Motor Core + + This code for the Arduino Robot's motor board + is the stock firmware. program the motor board with + this sketch whenever you want to return the motor + board to its default state. + +*/ + +#include + +void setup(){ + RobotMotor.begin(); +} +void loop(){ + RobotMotor.parseCommand(); + RobotMotor.process(); +} diff --git a/lineFollow.cpp b/lineFollow.cpp new file mode 100644 index 0000000..d6ebed8 --- /dev/null +++ b/lineFollow.cpp @@ -0,0 +1,152 @@ +//#include +#include "LineFollow.h" + +//#define KP 19 //0.1 units +//#define KD 14 +//#define ROBOT_SPEED 100 //percentage + +//#define KP 11 +//#define KD 5 +//#define ROBOT_SPEED 50 + +//#define INTEGRATION_TIME 10 //En ms + +/*uint8_t KP=11; +uint8_t KD=5; +uint8_t robotSpeed=50; //percentage +uint8_t intergrationTime=10;*/ + +#define NIVEL_PARA_LINEA 50 + +/*int lectura_sensor[5], last_error=0, acu=0; + +//Estos son los arrays que hay que rellenar con los valores de los sensores +//de suelo sobre blanco y negro. +int sensor_blanco[]={ + 0,0,0,0,0}; +int sensor_negro[]={ + 1023,1023,1023,1023,1023}; +*/ +//unsigned long time; + +//void mueve_robot(int vel_izq, int vel_der); +//void para_robot(); +//void doCalibration(int speedPct, int time); +//void ajusta_niveles(); //calibrate values + +LineFollow::LineFollow(){ + /*KP=11; + KD=5; + robotSpeed=50; //percentage + intergrationTime=10;*/ + config(11,5,50,10); + + for(int i=0;i<5;i++){ + sensor_blanco[i]=0; + sensor_negro[i]=1023; + } +} + +void LineFollow::config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime){ + this->KP=KP; + this->KD=KD; + this->robotSpeed=robotSpeed; + this->intergrationTime=intergrationTime; + /*Serial.print("LFC: "); + Serial.print(KP); + Serial.print(' '); + Serial.print(KD); + Serial.print(' '); + Serial.print(robotSpeed); + Serial.print(' '); + Serial.println(intergrationTime);*/ + +} +void LineFollow::calibIRs(){ + static bool isInited=false;//So only init once + if(isInited)return ; + + delay(1000); + + doCalibration(30,500); + doCalibration(-30,800); + doCalibration(30,500); + + delay(1000); + isInited=true; +} + +void LineFollow::runLineFollow(){ + for(int count=0; count<5; count++) + { + lectura_sensor[count]=map(IRread(count),sensor_negro[count],sensor_blanco[count],0,127); + acu+=lectura_sensor[count]; + } + + //Serial.println(millis()); + if (acu > NIVEL_PARA_LINEA) + { + acu/=5; + + int error = ((lectura_sensor[0]<<6)+(lectura_sensor[1]<<5)-(lectura_sensor[3]<<5)-(lectura_sensor[4]<<6))/acu; + + error = constrain(error,-100,100); + + //Calculamos la correcion de velocidad mediante un filtro PD + int vel = (error * KP)/10 + (error-last_error)*KD; + + last_error = error; + + //Corregimos la velocidad de avance con el error de salida del filtro PD + int motor_left = constrain((robotSpeed + vel),-100,100); + int motor_right =constrain((robotSpeed - vel),-100,100); + + //Movemos el robot + //motorsWritePct(motor_left,motor_right); + motorsWritePct(motor_left,motor_right); + + //Esperamos un poquito a que el robot reaccione + delay(intergrationTime); + } + else + { + //Hemos encontrado una linea negra + //perpendicular a nuestro camino + //paramos el robot + motorsStop(); + + //y detenemos la ejecución del programa + //while(true); + reportActionDone(); + //setMode(MODE_SIMPLE); + } +} + + +void LineFollow::doCalibration(int speedPct, int time){ + motorsWritePct(speedPct, -speedPct); + unsigned long beginTime = millis(); + while((millis()-beginTime) sensor_blanco[count]) + sensor_blanco[count]=lectura; + + if (lectura < sensor_negro[count]) + sensor_negro[count]=lectura; + } +} + + + + + + From 141de7af9acc8a2514e9b99910f7715e5aee7280 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 29 May 2013 18:30:36 +0200 Subject: [PATCH 16/67] Merged 1.0.5 --- ArduinoRobotMotorBoard.cpp | 265 ------------------ ArduinoRobotMotorBoard.h | 125 --------- EasyTransfer2.cpp | 152 ---------- EasyTransfer2.h | 76 ----- LineFollow.h | 40 --- Multiplexer.cpp | 37 --- Multiplexer.h | 24 -- .../Robot_IR_Array_Test.ino | 26 -- .../Robot_Motor_Core/Robot_Motor_Core.ino | 18 -- lineFollow.cpp | 152 ---------- 10 files changed, 915 deletions(-) delete mode 100644 ArduinoRobotMotorBoard.cpp delete mode 100644 ArduinoRobotMotorBoard.h delete mode 100644 EasyTransfer2.cpp delete mode 100644 EasyTransfer2.h delete mode 100644 LineFollow.h delete mode 100644 Multiplexer.cpp delete mode 100644 Multiplexer.h delete mode 100644 examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino delete mode 100644 examples/Robot_Motor_Core/Robot_Motor_Core.ino delete mode 100644 lineFollow.cpp diff --git a/ArduinoRobotMotorBoard.cpp b/ArduinoRobotMotorBoard.cpp deleted file mode 100644 index 7740a06..0000000 --- a/ArduinoRobotMotorBoard.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include "ArduinoRobotMotorBoard.h" -#include "EasyTransfer2.h" -#include "Multiplexer.h" -#include "LineFollow.h" - -RobotMotorBoard::RobotMotorBoard(){ - //LineFollow::LineFollow(); -} -/*void RobotMotorBoard::beginIRReceiver(){ - IRrecv::enableIRIn(); -}*/ -void RobotMotorBoard::begin(){ - //initialze communication - Serial1.begin(9600); - messageIn.begin(&Serial1); - messageOut.begin(&Serial1); - - //init MUX - uint8_t MuxPins[]={MUXA,MUXB,MUXC}; - this->IRs.begin(MuxPins,MUX_IN,3); - pinMode(MUXI,INPUT); - digitalWrite(MUXI,LOW); - - isPaused=false; -} - -void RobotMotorBoard::process(){ - if(isPaused)return;//skip process if the mode is paused - - if(mode==MODE_SIMPLE){ - //Serial.println("s"); - //do nothing? Simple mode is just about getting commands - }else if(mode==MODE_LINE_FOLLOW){ - //do line following stuff here. - LineFollow::runLineFollow(); - }else if(mode==MODE_ADJUST_MOTOR){ - //Serial.println('a'); - //motorAdjustment=analogRead(POT); - //setSpeed(255,255); - //delay(100); - } -} -void RobotMotorBoard::pauseMode(bool onOff){ - if(onOff){ - isPaused=true; - }else{ - isPaused=false; - } - stopCurrentActions(); - -} -void RobotMotorBoard::parseCommand(){ - uint8_t modeName; - uint8_t codename; - int value; - int speedL; - int speedR; - if(this->messageIn.receiveData()){ - //Serial.println("data received"); - uint8_t command=messageIn.readByte(); - //Serial.println(command); - switch(command){ - case COMMAND_SWITCH_MODE: - modeName=messageIn.readByte(); - setMode(modeName); - break; - case COMMAND_RUN: - if(mode==MODE_LINE_FOLLOW)break;//in follow line mode, the motor does not follow commands - speedL=messageIn.readInt(); - speedR=messageIn.readInt(); - motorsWrite(speedL,speedR); - break; - case COMMAND_MOTORS_STOP: - motorsStop(); - break; - case COMMAND_ANALOG_WRITE: - codename=messageIn.readByte(); - value=messageIn.readInt(); - _analogWrite(codename,value); - break; - case COMMAND_DIGITAL_WRITE: - codename=messageIn.readByte(); - value=messageIn.readByte(); - _digitalWrite(codename,value); - break; - case COMMAND_ANALOG_READ: - codename=messageIn.readByte(); - _analogRead(codename); - break; - case COMMAND_DIGITAL_READ: - codename=messageIn.readByte(); - _digitalRead(codename); - break; - case COMMAND_READ_IR: - _readIR(); - break; - case COMMAND_READ_TRIM: - _readTrim(); - break; - case COMMAND_PAUSE_MODE: - pauseMode(messageIn.readByte());//onOff state - break; - case COMMAND_LINE_FOLLOW_CONFIG: - LineFollow::config( - messageIn.readByte(), //KP - messageIn.readByte(), //KD - messageIn.readByte(), //robotSpeed - messageIn.readByte() //IntegrationTime - ); - break; - } - } - //delay(5); -} -uint8_t RobotMotorBoard::parseCodename(uint8_t codename){ - switch(codename){ - case B_TK1: - return TK1; - case B_TK2: - return TK2; - case B_TK3: - return TK3; - case B_TK4: - return TK4; - } -} -uint8_t RobotMotorBoard::codenameToAPin(uint8_t codename){ - switch(codename){ - case B_TK1: - return A0; - case B_TK2: - return A1; - case B_TK3: - return A6; - case B_TK4: - return A11; - } -} - -void RobotMotorBoard::setMode(uint8_t mode){ - if(mode==MODE_LINE_FOLLOW){ - LineFollow::calibIRs(); - } - /*if(mode==SET_MOTOR_ADJUSTMENT){ - save_motor_adjustment_to_EEPROM(); - } - */ - /*if(mode==MODE_IR_CONTROL){ - beginIRReceiver(); - }*/ - this->mode=mode; - //stopCurrentActions();//If line following, this should stop the motors -} - -void RobotMotorBoard::stopCurrentActions(){ - motorsStop(); - //motorsWrite(0,0); -} - -void RobotMotorBoard::motorsWrite(int speedL, int speedR){ - /*Serial.print(speedL); - Serial.print(" "); - Serial.println(speedR);*/ - //motor adjustment, using percentage - _refreshMotorAdjustment(); - - if(motorAdjustment<0){ - speedR*=(1+motorAdjustment); - }else{ - speedL*=(1-motorAdjustment); - } - - if(speedL>0){ - analogWrite(IN_A1,speedL); - analogWrite(IN_A2,0); - }else{ - analogWrite(IN_A1,0); - analogWrite(IN_A2,-speedL); - } - - if(speedR>0){ - analogWrite(IN_B1,speedR); - analogWrite(IN_B2,0); - }else{ - analogWrite(IN_B1,0); - analogWrite(IN_B2,-speedR); - } -} -void RobotMotorBoard::motorsWritePct(int speedLpct, int speedRpct){ - //speedLpct, speedRpct ranges from -100 to 100 - motorsWrite(speedLpct*2.55,speedRpct*2.55); -} -void RobotMotorBoard::motorsStop(){ - analogWrite(IN_A1,255); - analogWrite(IN_A2,255); - - analogWrite(IN_B1,255); - analogWrite(IN_B2,255); -} - - -/* -* -* -* Input and Output ports -* -* -*/ -void RobotMotorBoard::_digitalWrite(uint8_t codename,bool value){ - uint8_t pin=parseCodename(codename); - digitalWrite(pin,value); -} -void RobotMotorBoard::_analogWrite(uint8_t codename,int value){ - //There's no PWM available on motor board -} -void RobotMotorBoard::_digitalRead(uint8_t codename){ - uint8_t pin=parseCodename(codename); - bool value=digitalRead(pin); - messageOut.writeByte(COMMAND_DIGITAL_READ_RE); - messageOut.writeByte(codename); - messageOut.writeByte(value); - messageOut.sendData(); -} -void RobotMotorBoard::_analogRead(uint8_t codename){ - uint8_t pin=codenameToAPin(codename); - int value=analogRead(pin); - messageOut.writeByte(COMMAND_ANALOG_READ_RE); - messageOut.writeByte(codename); - messageOut.writeInt(value); - messageOut.sendData(); -} -int RobotMotorBoard::IRread(uint8_t num){ - IRs.selectPin(num-1); //To make consistant with the pins labeled on the board - return IRs.getAnalogValue(); -} - -void RobotMotorBoard::_readIR(){ - //Serial.println("readIR"); - int value; - messageOut.writeByte(COMMAND_READ_IR_RE); - for(int i=1;i<6;i++){ - value=IRread(i); - messageOut.writeInt(value); - } - messageOut.sendData(); -} - -void RobotMotorBoard::_readTrim(){ - int value=analogRead(TRIM); - messageOut.writeByte(COMMAND_READ_TRIM_RE); - messageOut.writeInt(value); - messageOut.sendData(); -} - -void RobotMotorBoard::_refreshMotorAdjustment(){ - motorAdjustment=map(analogRead(TRIM),0,1023,-30,30)/100.0; -} - -void RobotMotorBoard::reportActionDone(){ - setMode(MODE_SIMPLE); - messageOut.writeByte(COMMAND_ACTION_DONE); - messageOut.sendData(); -} - -RobotMotorBoard RobotMotor=RobotMotorBoard(); \ No newline at end of file diff --git a/ArduinoRobotMotorBoard.h b/ArduinoRobotMotorBoard.h deleted file mode 100644 index c1004c4..0000000 --- a/ArduinoRobotMotorBoard.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef ArduinoRobot_h -#define ArduinoRobot_h - -#include "EasyTransfer2.h" -#include "Multiplexer.h" -#include "LineFollow.h" -//#include "IRremote.h" - -#if ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -//Command code -#define COMMAND_SWITCH_MODE 0 -#define COMMAND_RUN 10 -#define COMMAND_MOTORS_STOP 11 -#define COMMAND_ANALOG_WRITE 20 -#define COMMAND_DIGITAL_WRITE 30 -#define COMMAND_ANALOG_READ 40 -#define COMMAND_ANALOG_READ_RE 41 -#define COMMAND_DIGITAL_READ 50 -#define COMMAND_DIGITAL_READ_RE 51 -#define COMMAND_READ_IR 60 -#define COMMAND_READ_IR_RE 61 -#define COMMAND_ACTION_DONE 70 -#define COMMAND_READ_TRIM 80 -#define COMMAND_READ_TRIM_RE 81 -#define COMMAND_PAUSE_MODE 90 -#define COMMAND_LINE_FOLLOW_CONFIG 100 - - -//component codename -#define CN_LEFT_MOTOR 0 -#define CN_RIGHT_MOTOR 1 -#define CN_IR 2 - -//motor board modes -#define MODE_SIMPLE 0 -#define MODE_LINE_FOLLOW 1 -#define MODE_ADJUST_MOTOR 2 -#define MODE_IR_CONTROL 3 - -//bottom TKs, just for communication purpose -#define B_TK1 201 -#define B_TK2 202 -#define B_TK3 203 -#define B_TK4 204 - -/* -A message structure will be: -switch mode (2): - byte COMMAND_SWITCH_MODE, byte mode -run (5): - byte COMMAND_RUN, int speedL, int speedR -analogWrite (3): - byte COMMAND_ANALOG_WRITE, byte codename, byte value; -digitalWrite (3): - byte COMMAND_DIGITAL_WRITE, byte codename, byte value; -analogRead (2): - byte COMMAND_ANALOG_READ, byte codename; -analogRead _return_ (4): - byte COMMAND_ANALOG_READ_RE, byte codename, int value; -digitalRead (2): - byte COMMAND_DIGITAL_READ, byte codename; -digitalRead _return_ (4): - byte COMMAND_DIGITAL_READ_RE, byte codename, int value; -read IR (1): - byte COMMAND_READ_IR; -read IR _return_ (9): - byte COMMAND_READ_IR_RE, int valueA, int valueB, int valueC, int valueD; - - -*/ - -class RobotMotorBoard:public LineFollow{ - public: - RobotMotorBoard(); - void begin(); - - void process(); - - void parseCommand(); - - int IRread(uint8_t num); - - void setMode(uint8_t mode); - void pauseMode(bool onOff); - - void motorsWrite(int speedL, int speedR); - void motorsWritePct(int speedLpct, int speedRpct);//write motor values in percentage - void motorsStop(); - private: - float motorAdjustment;//-1.0 ~ 1.0, whether left is lowered or right is lowered - - //convert codename to actual pins - uint8_t parseCodename(uint8_t codename); - uint8_t codenameToAPin(uint8_t codename); - - void stopCurrentActions(); - //void sendCommand(byte command,byte codename,int value); - - void _analogWrite(uint8_t codename, int value); - void _digitalWrite(uint8_t codename, bool value); - void _analogRead(uint8_t codename); - void _digitalRead(uint8_t codename); - void _readIR(); - void _readTrim(); - - void _refreshMotorAdjustment(); - - Multiplexer IRs; - uint8_t mode; - uint8_t isPaused; - EasyTransfer2 messageIn; - EasyTransfer2 messageOut; - - //Line Following - void reportActionDone(); -}; - -extern RobotMotorBoard RobotMotor; - -#endif \ No newline at end of file diff --git a/EasyTransfer2.cpp b/EasyTransfer2.cpp deleted file mode 100644 index 24427cc..0000000 --- a/EasyTransfer2.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "EasyTransfer2.h" - - - - -//Captures address and size of struct -void EasyTransfer2::begin(HardwareSerial *theSerial){ - _serial = theSerial; - - //dynamic creation of rx parsing buffer in RAM - //rx_buffer = (uint8_t*) malloc(size); - - resetData(); -} - -void EasyTransfer2::writeByte(uint8_t dat){ - if(position<20) - data[position++]=dat; - size++; -} -void EasyTransfer2::writeInt(int dat){ - if(position<19){ - data[position++]=dat>>8; - data[position++]=dat; - size+=2; - } -} -uint8_t EasyTransfer2::readByte(){ - if(position>=size)return 0; - return data[position++]; -} -int EasyTransfer2::readInt(){ - if(position+1>=size)return 0; - int dat_1=data[position++]<<8; - int dat_2=data[position++]; - int dat= dat_1 | dat_2; - return dat; -} - -void EasyTransfer2::resetData(){ - for(int i=0;i<20;i++){ - data[i]=0; - } - size=0; - position=0; -} - -//Sends out struct in binary, with header, length info and checksum -void EasyTransfer2::sendData(){ - uint8_t CS = size; - _serial->write(0x06); - _serial->write(0x85); - _serial->write(size); - for(int i = 0; iwrite(*(data+i)); - //Serial.print(*(data+i)); - //Serial.print(","); - } - //Serial.println(""); - _serial->write(CS); - - resetData(); -} - -boolean EasyTransfer2::receiveData(){ - - //start off by looking for the header bytes. If they were already found in a previous call, skip it. - if(rx_len == 0){ - //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. - if(_serial->available() >= 3){ - //this will block until a 0x06 is found or buffer size becomes less then 3. - while(_serial->read() != 0x06) { - //This will trash any preamble junk in the serial buffer - //but we need to make sure there is enough in the buffer to process while we trash the rest - //if the buffer becomes too empty, we will escape and try again on the next call - if(_serial->available() < 3) - return false; - } - //Serial.println("head"); - if (_serial->read() == 0x85){ - rx_len = _serial->read(); - //Serial.print("rx_len:"); - //Serial.println(rx_len); - resetData(); - - //make sure the binary structs on both Arduinos are the same size. - /*if(rx_len != size){ - rx_len = 0; - return false; - }*/ - } - } - //Serial.println("nothing"); - } - - //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. - if(rx_len != 0){ - - while(_serial->available() && rx_array_inx <= rx_len){ - data[rx_array_inx++] = _serial->read(); - } - - if(rx_len == (rx_array_inx-1)){ - //seem to have got whole message - //last uint8_t is CS - calc_CS = rx_len; - //Serial.print("len:"); - //Serial.println(rx_len); - for (int i = 0; i -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ -#ifndef EasyTransfer2_h -#define EasyTransfer2_h - - -//make it a little prettier on the front end. -#define details(name) (byte*)&name,sizeof(name) - -//Not neccessary, but just in case. -#if ARDUINO > 22 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif -#include "HardwareSerial.h" -//#include -#include -#include -#include -#include - -class EasyTransfer2 { -public: -void begin(HardwareSerial *theSerial); -//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); -void sendData(); -boolean receiveData(); - -void writeByte(uint8_t dat); -void writeInt(int dat); -uint8_t readByte(); -int readInt(); - - -private: -HardwareSerial *_serial; - -void resetData(); - -uint8_t data[20]; //data storage, for both read and send -uint8_t position; -uint8_t size; //size of data in bytes. Both for read and send -//uint8_t * address; //address of struct -//uint8_t size; //size of struct -//uint8_t * rx_buffer; //address for temporary storage and parsing buffer -//uint8_t rx_buffer[20]; -uint8_t rx_array_inx; //index for RX parsing buffer -uint8_t rx_len; //RX packet length according to the packet -uint8_t calc_CS; //calculated Chacksum -}; - - - -#endif \ No newline at end of file diff --git a/LineFollow.h b/LineFollow.h deleted file mode 100644 index 608d573..0000000 --- a/LineFollow.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef LINE_FOLLOW_H -#define LINE_FOLLOW_H - -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -class LineFollow{ - public: - LineFollow(); - - void calibIRs(); - void runLineFollow(); - void config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime); - - //These are all pure virtual functions, pure VF needs pure specifier "=0" - //virtual void motorsWrite(int speedL, int speedR)=0; - virtual void motorsWritePct(int speedLpct, int speedRpct)=0; - virtual void motorsStop()=0; - virtual int IRread(uint8_t num)=0; - protected: - virtual void reportActionDone()=0; - - private: - void doCalibration(int speedPct, int time); - void ajusta_niveles(); - - uint8_t KP; - uint8_t KD; - uint8_t robotSpeed; //percentage - uint8_t intergrationTime; - - int lectura_sensor[5], last_error, acu; - int sensor_blanco[5]; - int sensor_negro[5]; -}; - -#endif \ No newline at end of file diff --git a/Multiplexer.cpp b/Multiplexer.cpp deleted file mode 100644 index c0fdd86..0000000 --- a/Multiplexer.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "Multiplexer.h" - -void Multiplexer::begin(uint8_t* selectors, uint8_t Z, uint8_t length){ - for(uint8_t i=0;iselectors[i]=selectors[i]; - pinMode(selectors[i],OUTPUT); - } - this->length=length; - this->pin_Z=Z; - pinMode(pin_Z,INPUT); -} - -void Multiplexer::selectPin(uint8_t num){ - for(uint8_t i=0;i= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -class Multiplexer{ - public: - void begin(uint8_t* selectors, uint8_t Z, uint8_t length); - void selectPin(uint8_t num); - int getAnalogValue(); - int getAnalogValueAt(uint8_t num); - bool getDigitalValue(); - bool getDigitalValueAt(uint8_t num); - private: - uint8_t selectors[4]; - uint8_t pin_Z; - uint8_t length; -}; - -#endif diff --git a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino deleted file mode 100644 index e201fd9..0000000 --- a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino +++ /dev/null @@ -1,26 +0,0 @@ -/* Motor Board IR Array Test - - This example of the Arduno robot's motor board returns the - values read fron the 5 infrared sendors on the bottom of - the robot. - -*/ -// include the motor board header -#include - -String bar; // string for storing the informaton - -void setup(){ - // start serial communication - Serial.begin(9600); - // initialize the library - RobotMotor.begin(); -} -void loop(){ - bar=String(""); // empty the string - // read the sensors and add them to the string - bar=bar+RobotMotor.readIR(0)+' '+RobotMotor.readIR(1)+' '+RobotMotor.readIR(2)+' '+RobotMotor.readIR(3)+' '+RobotMotor.readIR(4); - // print out the values - Serial.println(bar); - delay(100); -} diff --git a/examples/Robot_Motor_Core/Robot_Motor_Core.ino b/examples/Robot_Motor_Core/Robot_Motor_Core.ino deleted file mode 100644 index f74f30a..0000000 --- a/examples/Robot_Motor_Core/Robot_Motor_Core.ino +++ /dev/null @@ -1,18 +0,0 @@ -/* Motor Core - - This code for the Arduino Robot's motor board - is the stock firmware. program the motor board with - this sketch whenever you want to return the motor - board to its default state. - -*/ - -#include - -void setup(){ - RobotMotor.begin(); -} -void loop(){ - RobotMotor.parseCommand(); - RobotMotor.process(); -} diff --git a/lineFollow.cpp b/lineFollow.cpp deleted file mode 100644 index d6ebed8..0000000 --- a/lineFollow.cpp +++ /dev/null @@ -1,152 +0,0 @@ -//#include -#include "LineFollow.h" - -//#define KP 19 //0.1 units -//#define KD 14 -//#define ROBOT_SPEED 100 //percentage - -//#define KP 11 -//#define KD 5 -//#define ROBOT_SPEED 50 - -//#define INTEGRATION_TIME 10 //En ms - -/*uint8_t KP=11; -uint8_t KD=5; -uint8_t robotSpeed=50; //percentage -uint8_t intergrationTime=10;*/ - -#define NIVEL_PARA_LINEA 50 - -/*int lectura_sensor[5], last_error=0, acu=0; - -//Estos son los arrays que hay que rellenar con los valores de los sensores -//de suelo sobre blanco y negro. -int sensor_blanco[]={ - 0,0,0,0,0}; -int sensor_negro[]={ - 1023,1023,1023,1023,1023}; -*/ -//unsigned long time; - -//void mueve_robot(int vel_izq, int vel_der); -//void para_robot(); -//void doCalibration(int speedPct, int time); -//void ajusta_niveles(); //calibrate values - -LineFollow::LineFollow(){ - /*KP=11; - KD=5; - robotSpeed=50; //percentage - intergrationTime=10;*/ - config(11,5,50,10); - - for(int i=0;i<5;i++){ - sensor_blanco[i]=0; - sensor_negro[i]=1023; - } -} - -void LineFollow::config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime){ - this->KP=KP; - this->KD=KD; - this->robotSpeed=robotSpeed; - this->intergrationTime=intergrationTime; - /*Serial.print("LFC: "); - Serial.print(KP); - Serial.print(' '); - Serial.print(KD); - Serial.print(' '); - Serial.print(robotSpeed); - Serial.print(' '); - Serial.println(intergrationTime);*/ - -} -void LineFollow::calibIRs(){ - static bool isInited=false;//So only init once - if(isInited)return ; - - delay(1000); - - doCalibration(30,500); - doCalibration(-30,800); - doCalibration(30,500); - - delay(1000); - isInited=true; -} - -void LineFollow::runLineFollow(){ - for(int count=0; count<5; count++) - { - lectura_sensor[count]=map(IRread(count),sensor_negro[count],sensor_blanco[count],0,127); - acu+=lectura_sensor[count]; - } - - //Serial.println(millis()); - if (acu > NIVEL_PARA_LINEA) - { - acu/=5; - - int error = ((lectura_sensor[0]<<6)+(lectura_sensor[1]<<5)-(lectura_sensor[3]<<5)-(lectura_sensor[4]<<6))/acu; - - error = constrain(error,-100,100); - - //Calculamos la correcion de velocidad mediante un filtro PD - int vel = (error * KP)/10 + (error-last_error)*KD; - - last_error = error; - - //Corregimos la velocidad de avance con el error de salida del filtro PD - int motor_left = constrain((robotSpeed + vel),-100,100); - int motor_right =constrain((robotSpeed - vel),-100,100); - - //Movemos el robot - //motorsWritePct(motor_left,motor_right); - motorsWritePct(motor_left,motor_right); - - //Esperamos un poquito a que el robot reaccione - delay(intergrationTime); - } - else - { - //Hemos encontrado una linea negra - //perpendicular a nuestro camino - //paramos el robot - motorsStop(); - - //y detenemos la ejecución del programa - //while(true); - reportActionDone(); - //setMode(MODE_SIMPLE); - } -} - - -void LineFollow::doCalibration(int speedPct, int time){ - motorsWritePct(speedPct, -speedPct); - unsigned long beginTime = millis(); - while((millis()-beginTime) sensor_blanco[count]) - sensor_blanco[count]=lectura; - - if (lectura < sensor_negro[count]) - sensor_negro[count]=lectura; - } -} - - - - - - From ea5299ec9ac5f0ead4843af165c3f0a385b9d9a8 Mon Sep 17 00:00:00 2001 From: Fede85 Date: Wed, 3 Jul 2013 22:00:02 +0200 Subject: [PATCH 17/67] SPI library to the new format and moved Robot_Motor and Robot_Control libraries --- ArduinoRobotMotorBoard.cpp | 265 ++++++++++++++++++ ArduinoRobotMotorBoard.h | 125 +++++++++ EasyTransfer2.cpp | 152 ++++++++++ EasyTransfer2.h | 76 +++++ LineFollow.h | 40 +++ Multiplexer.cpp | 37 +++ Multiplexer.h | 24 ++ .../Robot_IR_Array_Test.ino | 26 ++ .../Robot_Motor_Core/Robot_Motor_Core.ino | 18 ++ lineFollow.cpp | 152 ++++++++++ 10 files changed, 915 insertions(+) create mode 100644 ArduinoRobotMotorBoard.cpp create mode 100644 ArduinoRobotMotorBoard.h create mode 100644 EasyTransfer2.cpp create mode 100644 EasyTransfer2.h create mode 100644 LineFollow.h create mode 100644 Multiplexer.cpp create mode 100644 Multiplexer.h create mode 100644 examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino create mode 100644 examples/Robot_Motor_Core/Robot_Motor_Core.ino create mode 100644 lineFollow.cpp diff --git a/ArduinoRobotMotorBoard.cpp b/ArduinoRobotMotorBoard.cpp new file mode 100644 index 0000000..7740a06 --- /dev/null +++ b/ArduinoRobotMotorBoard.cpp @@ -0,0 +1,265 @@ +#include "ArduinoRobotMotorBoard.h" +#include "EasyTransfer2.h" +#include "Multiplexer.h" +#include "LineFollow.h" + +RobotMotorBoard::RobotMotorBoard(){ + //LineFollow::LineFollow(); +} +/*void RobotMotorBoard::beginIRReceiver(){ + IRrecv::enableIRIn(); +}*/ +void RobotMotorBoard::begin(){ + //initialze communication + Serial1.begin(9600); + messageIn.begin(&Serial1); + messageOut.begin(&Serial1); + + //init MUX + uint8_t MuxPins[]={MUXA,MUXB,MUXC}; + this->IRs.begin(MuxPins,MUX_IN,3); + pinMode(MUXI,INPUT); + digitalWrite(MUXI,LOW); + + isPaused=false; +} + +void RobotMotorBoard::process(){ + if(isPaused)return;//skip process if the mode is paused + + if(mode==MODE_SIMPLE){ + //Serial.println("s"); + //do nothing? Simple mode is just about getting commands + }else if(mode==MODE_LINE_FOLLOW){ + //do line following stuff here. + LineFollow::runLineFollow(); + }else if(mode==MODE_ADJUST_MOTOR){ + //Serial.println('a'); + //motorAdjustment=analogRead(POT); + //setSpeed(255,255); + //delay(100); + } +} +void RobotMotorBoard::pauseMode(bool onOff){ + if(onOff){ + isPaused=true; + }else{ + isPaused=false; + } + stopCurrentActions(); + +} +void RobotMotorBoard::parseCommand(){ + uint8_t modeName; + uint8_t codename; + int value; + int speedL; + int speedR; + if(this->messageIn.receiveData()){ + //Serial.println("data received"); + uint8_t command=messageIn.readByte(); + //Serial.println(command); + switch(command){ + case COMMAND_SWITCH_MODE: + modeName=messageIn.readByte(); + setMode(modeName); + break; + case COMMAND_RUN: + if(mode==MODE_LINE_FOLLOW)break;//in follow line mode, the motor does not follow commands + speedL=messageIn.readInt(); + speedR=messageIn.readInt(); + motorsWrite(speedL,speedR); + break; + case COMMAND_MOTORS_STOP: + motorsStop(); + break; + case COMMAND_ANALOG_WRITE: + codename=messageIn.readByte(); + value=messageIn.readInt(); + _analogWrite(codename,value); + break; + case COMMAND_DIGITAL_WRITE: + codename=messageIn.readByte(); + value=messageIn.readByte(); + _digitalWrite(codename,value); + break; + case COMMAND_ANALOG_READ: + codename=messageIn.readByte(); + _analogRead(codename); + break; + case COMMAND_DIGITAL_READ: + codename=messageIn.readByte(); + _digitalRead(codename); + break; + case COMMAND_READ_IR: + _readIR(); + break; + case COMMAND_READ_TRIM: + _readTrim(); + break; + case COMMAND_PAUSE_MODE: + pauseMode(messageIn.readByte());//onOff state + break; + case COMMAND_LINE_FOLLOW_CONFIG: + LineFollow::config( + messageIn.readByte(), //KP + messageIn.readByte(), //KD + messageIn.readByte(), //robotSpeed + messageIn.readByte() //IntegrationTime + ); + break; + } + } + //delay(5); +} +uint8_t RobotMotorBoard::parseCodename(uint8_t codename){ + switch(codename){ + case B_TK1: + return TK1; + case B_TK2: + return TK2; + case B_TK3: + return TK3; + case B_TK4: + return TK4; + } +} +uint8_t RobotMotorBoard::codenameToAPin(uint8_t codename){ + switch(codename){ + case B_TK1: + return A0; + case B_TK2: + return A1; + case B_TK3: + return A6; + case B_TK4: + return A11; + } +} + +void RobotMotorBoard::setMode(uint8_t mode){ + if(mode==MODE_LINE_FOLLOW){ + LineFollow::calibIRs(); + } + /*if(mode==SET_MOTOR_ADJUSTMENT){ + save_motor_adjustment_to_EEPROM(); + } + */ + /*if(mode==MODE_IR_CONTROL){ + beginIRReceiver(); + }*/ + this->mode=mode; + //stopCurrentActions();//If line following, this should stop the motors +} + +void RobotMotorBoard::stopCurrentActions(){ + motorsStop(); + //motorsWrite(0,0); +} + +void RobotMotorBoard::motorsWrite(int speedL, int speedR){ + /*Serial.print(speedL); + Serial.print(" "); + Serial.println(speedR);*/ + //motor adjustment, using percentage + _refreshMotorAdjustment(); + + if(motorAdjustment<0){ + speedR*=(1+motorAdjustment); + }else{ + speedL*=(1-motorAdjustment); + } + + if(speedL>0){ + analogWrite(IN_A1,speedL); + analogWrite(IN_A2,0); + }else{ + analogWrite(IN_A1,0); + analogWrite(IN_A2,-speedL); + } + + if(speedR>0){ + analogWrite(IN_B1,speedR); + analogWrite(IN_B2,0); + }else{ + analogWrite(IN_B1,0); + analogWrite(IN_B2,-speedR); + } +} +void RobotMotorBoard::motorsWritePct(int speedLpct, int speedRpct){ + //speedLpct, speedRpct ranges from -100 to 100 + motorsWrite(speedLpct*2.55,speedRpct*2.55); +} +void RobotMotorBoard::motorsStop(){ + analogWrite(IN_A1,255); + analogWrite(IN_A2,255); + + analogWrite(IN_B1,255); + analogWrite(IN_B2,255); +} + + +/* +* +* +* Input and Output ports +* +* +*/ +void RobotMotorBoard::_digitalWrite(uint8_t codename,bool value){ + uint8_t pin=parseCodename(codename); + digitalWrite(pin,value); +} +void RobotMotorBoard::_analogWrite(uint8_t codename,int value){ + //There's no PWM available on motor board +} +void RobotMotorBoard::_digitalRead(uint8_t codename){ + uint8_t pin=parseCodename(codename); + bool value=digitalRead(pin); + messageOut.writeByte(COMMAND_DIGITAL_READ_RE); + messageOut.writeByte(codename); + messageOut.writeByte(value); + messageOut.sendData(); +} +void RobotMotorBoard::_analogRead(uint8_t codename){ + uint8_t pin=codenameToAPin(codename); + int value=analogRead(pin); + messageOut.writeByte(COMMAND_ANALOG_READ_RE); + messageOut.writeByte(codename); + messageOut.writeInt(value); + messageOut.sendData(); +} +int RobotMotorBoard::IRread(uint8_t num){ + IRs.selectPin(num-1); //To make consistant with the pins labeled on the board + return IRs.getAnalogValue(); +} + +void RobotMotorBoard::_readIR(){ + //Serial.println("readIR"); + int value; + messageOut.writeByte(COMMAND_READ_IR_RE); + for(int i=1;i<6;i++){ + value=IRread(i); + messageOut.writeInt(value); + } + messageOut.sendData(); +} + +void RobotMotorBoard::_readTrim(){ + int value=analogRead(TRIM); + messageOut.writeByte(COMMAND_READ_TRIM_RE); + messageOut.writeInt(value); + messageOut.sendData(); +} + +void RobotMotorBoard::_refreshMotorAdjustment(){ + motorAdjustment=map(analogRead(TRIM),0,1023,-30,30)/100.0; +} + +void RobotMotorBoard::reportActionDone(){ + setMode(MODE_SIMPLE); + messageOut.writeByte(COMMAND_ACTION_DONE); + messageOut.sendData(); +} + +RobotMotorBoard RobotMotor=RobotMotorBoard(); \ No newline at end of file diff --git a/ArduinoRobotMotorBoard.h b/ArduinoRobotMotorBoard.h new file mode 100644 index 0000000..c1004c4 --- /dev/null +++ b/ArduinoRobotMotorBoard.h @@ -0,0 +1,125 @@ +#ifndef ArduinoRobot_h +#define ArduinoRobot_h + +#include "EasyTransfer2.h" +#include "Multiplexer.h" +#include "LineFollow.h" +//#include "IRremote.h" + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +//Command code +#define COMMAND_SWITCH_MODE 0 +#define COMMAND_RUN 10 +#define COMMAND_MOTORS_STOP 11 +#define COMMAND_ANALOG_WRITE 20 +#define COMMAND_DIGITAL_WRITE 30 +#define COMMAND_ANALOG_READ 40 +#define COMMAND_ANALOG_READ_RE 41 +#define COMMAND_DIGITAL_READ 50 +#define COMMAND_DIGITAL_READ_RE 51 +#define COMMAND_READ_IR 60 +#define COMMAND_READ_IR_RE 61 +#define COMMAND_ACTION_DONE 70 +#define COMMAND_READ_TRIM 80 +#define COMMAND_READ_TRIM_RE 81 +#define COMMAND_PAUSE_MODE 90 +#define COMMAND_LINE_FOLLOW_CONFIG 100 + + +//component codename +#define CN_LEFT_MOTOR 0 +#define CN_RIGHT_MOTOR 1 +#define CN_IR 2 + +//motor board modes +#define MODE_SIMPLE 0 +#define MODE_LINE_FOLLOW 1 +#define MODE_ADJUST_MOTOR 2 +#define MODE_IR_CONTROL 3 + +//bottom TKs, just for communication purpose +#define B_TK1 201 +#define B_TK2 202 +#define B_TK3 203 +#define B_TK4 204 + +/* +A message structure will be: +switch mode (2): + byte COMMAND_SWITCH_MODE, byte mode +run (5): + byte COMMAND_RUN, int speedL, int speedR +analogWrite (3): + byte COMMAND_ANALOG_WRITE, byte codename, byte value; +digitalWrite (3): + byte COMMAND_DIGITAL_WRITE, byte codename, byte value; +analogRead (2): + byte COMMAND_ANALOG_READ, byte codename; +analogRead _return_ (4): + byte COMMAND_ANALOG_READ_RE, byte codename, int value; +digitalRead (2): + byte COMMAND_DIGITAL_READ, byte codename; +digitalRead _return_ (4): + byte COMMAND_DIGITAL_READ_RE, byte codename, int value; +read IR (1): + byte COMMAND_READ_IR; +read IR _return_ (9): + byte COMMAND_READ_IR_RE, int valueA, int valueB, int valueC, int valueD; + + +*/ + +class RobotMotorBoard:public LineFollow{ + public: + RobotMotorBoard(); + void begin(); + + void process(); + + void parseCommand(); + + int IRread(uint8_t num); + + void setMode(uint8_t mode); + void pauseMode(bool onOff); + + void motorsWrite(int speedL, int speedR); + void motorsWritePct(int speedLpct, int speedRpct);//write motor values in percentage + void motorsStop(); + private: + float motorAdjustment;//-1.0 ~ 1.0, whether left is lowered or right is lowered + + //convert codename to actual pins + uint8_t parseCodename(uint8_t codename); + uint8_t codenameToAPin(uint8_t codename); + + void stopCurrentActions(); + //void sendCommand(byte command,byte codename,int value); + + void _analogWrite(uint8_t codename, int value); + void _digitalWrite(uint8_t codename, bool value); + void _analogRead(uint8_t codename); + void _digitalRead(uint8_t codename); + void _readIR(); + void _readTrim(); + + void _refreshMotorAdjustment(); + + Multiplexer IRs; + uint8_t mode; + uint8_t isPaused; + EasyTransfer2 messageIn; + EasyTransfer2 messageOut; + + //Line Following + void reportActionDone(); +}; + +extern RobotMotorBoard RobotMotor; + +#endif \ No newline at end of file diff --git a/EasyTransfer2.cpp b/EasyTransfer2.cpp new file mode 100644 index 0000000..24427cc --- /dev/null +++ b/EasyTransfer2.cpp @@ -0,0 +1,152 @@ +#include "EasyTransfer2.h" + + + + +//Captures address and size of struct +void EasyTransfer2::begin(HardwareSerial *theSerial){ + _serial = theSerial; + + //dynamic creation of rx parsing buffer in RAM + //rx_buffer = (uint8_t*) malloc(size); + + resetData(); +} + +void EasyTransfer2::writeByte(uint8_t dat){ + if(position<20) + data[position++]=dat; + size++; +} +void EasyTransfer2::writeInt(int dat){ + if(position<19){ + data[position++]=dat>>8; + data[position++]=dat; + size+=2; + } +} +uint8_t EasyTransfer2::readByte(){ + if(position>=size)return 0; + return data[position++]; +} +int EasyTransfer2::readInt(){ + if(position+1>=size)return 0; + int dat_1=data[position++]<<8; + int dat_2=data[position++]; + int dat= dat_1 | dat_2; + return dat; +} + +void EasyTransfer2::resetData(){ + for(int i=0;i<20;i++){ + data[i]=0; + } + size=0; + position=0; +} + +//Sends out struct in binary, with header, length info and checksum +void EasyTransfer2::sendData(){ + uint8_t CS = size; + _serial->write(0x06); + _serial->write(0x85); + _serial->write(size); + for(int i = 0; iwrite(*(data+i)); + //Serial.print(*(data+i)); + //Serial.print(","); + } + //Serial.println(""); + _serial->write(CS); + + resetData(); +} + +boolean EasyTransfer2::receiveData(){ + + //start off by looking for the header bytes. If they were already found in a previous call, skip it. + if(rx_len == 0){ + //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. + if(_serial->available() >= 3){ + //this will block until a 0x06 is found or buffer size becomes less then 3. + while(_serial->read() != 0x06) { + //This will trash any preamble junk in the serial buffer + //but we need to make sure there is enough in the buffer to process while we trash the rest + //if the buffer becomes too empty, we will escape and try again on the next call + if(_serial->available() < 3) + return false; + } + //Serial.println("head"); + if (_serial->read() == 0x85){ + rx_len = _serial->read(); + //Serial.print("rx_len:"); + //Serial.println(rx_len); + resetData(); + + //make sure the binary structs on both Arduinos are the same size. + /*if(rx_len != size){ + rx_len = 0; + return false; + }*/ + } + } + //Serial.println("nothing"); + } + + //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. + if(rx_len != 0){ + + while(_serial->available() && rx_array_inx <= rx_len){ + data[rx_array_inx++] = _serial->read(); + } + + if(rx_len == (rx_array_inx-1)){ + //seem to have got whole message + //last uint8_t is CS + calc_CS = rx_len; + //Serial.print("len:"); + //Serial.println(rx_len); + for (int i = 0; i +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ +#ifndef EasyTransfer2_h +#define EasyTransfer2_h + + +//make it a little prettier on the front end. +#define details(name) (byte*)&name,sizeof(name) + +//Not neccessary, but just in case. +#if ARDUINO > 22 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif +#include "HardwareSerial.h" +//#include +#include +#include +#include +#include + +class EasyTransfer2 { +public: +void begin(HardwareSerial *theSerial); +//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); +void sendData(); +boolean receiveData(); + +void writeByte(uint8_t dat); +void writeInt(int dat); +uint8_t readByte(); +int readInt(); + + +private: +HardwareSerial *_serial; + +void resetData(); + +uint8_t data[20]; //data storage, for both read and send +uint8_t position; +uint8_t size; //size of data in bytes. Both for read and send +//uint8_t * address; //address of struct +//uint8_t size; //size of struct +//uint8_t * rx_buffer; //address for temporary storage and parsing buffer +//uint8_t rx_buffer[20]; +uint8_t rx_array_inx; //index for RX parsing buffer +uint8_t rx_len; //RX packet length according to the packet +uint8_t calc_CS; //calculated Chacksum +}; + + + +#endif \ No newline at end of file diff --git a/LineFollow.h b/LineFollow.h new file mode 100644 index 0000000..608d573 --- /dev/null +++ b/LineFollow.h @@ -0,0 +1,40 @@ +#ifndef LINE_FOLLOW_H +#define LINE_FOLLOW_H + +#if ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +class LineFollow{ + public: + LineFollow(); + + void calibIRs(); + void runLineFollow(); + void config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime); + + //These are all pure virtual functions, pure VF needs pure specifier "=0" + //virtual void motorsWrite(int speedL, int speedR)=0; + virtual void motorsWritePct(int speedLpct, int speedRpct)=0; + virtual void motorsStop()=0; + virtual int IRread(uint8_t num)=0; + protected: + virtual void reportActionDone()=0; + + private: + void doCalibration(int speedPct, int time); + void ajusta_niveles(); + + uint8_t KP; + uint8_t KD; + uint8_t robotSpeed; //percentage + uint8_t intergrationTime; + + int lectura_sensor[5], last_error, acu; + int sensor_blanco[5]; + int sensor_negro[5]; +}; + +#endif \ No newline at end of file diff --git a/Multiplexer.cpp b/Multiplexer.cpp new file mode 100644 index 0000000..c0fdd86 --- /dev/null +++ b/Multiplexer.cpp @@ -0,0 +1,37 @@ +#include "Multiplexer.h" + +void Multiplexer::begin(uint8_t* selectors, uint8_t Z, uint8_t length){ + for(uint8_t i=0;iselectors[i]=selectors[i]; + pinMode(selectors[i],OUTPUT); + } + this->length=length; + this->pin_Z=Z; + pinMode(pin_Z,INPUT); +} + +void Multiplexer::selectPin(uint8_t num){ + for(uint8_t i=0;i= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +class Multiplexer{ + public: + void begin(uint8_t* selectors, uint8_t Z, uint8_t length); + void selectPin(uint8_t num); + int getAnalogValue(); + int getAnalogValueAt(uint8_t num); + bool getDigitalValue(); + bool getDigitalValueAt(uint8_t num); + private: + uint8_t selectors[4]; + uint8_t pin_Z; + uint8_t length; +}; + +#endif diff --git a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino new file mode 100644 index 0000000..e201fd9 --- /dev/null +++ b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino @@ -0,0 +1,26 @@ +/* Motor Board IR Array Test + + This example of the Arduno robot's motor board returns the + values read fron the 5 infrared sendors on the bottom of + the robot. + +*/ +// include the motor board header +#include + +String bar; // string for storing the informaton + +void setup(){ + // start serial communication + Serial.begin(9600); + // initialize the library + RobotMotor.begin(); +} +void loop(){ + bar=String(""); // empty the string + // read the sensors and add them to the string + bar=bar+RobotMotor.readIR(0)+' '+RobotMotor.readIR(1)+' '+RobotMotor.readIR(2)+' '+RobotMotor.readIR(3)+' '+RobotMotor.readIR(4); + // print out the values + Serial.println(bar); + delay(100); +} diff --git a/examples/Robot_Motor_Core/Robot_Motor_Core.ino b/examples/Robot_Motor_Core/Robot_Motor_Core.ino new file mode 100644 index 0000000..f74f30a --- /dev/null +++ b/examples/Robot_Motor_Core/Robot_Motor_Core.ino @@ -0,0 +1,18 @@ +/* Motor Core + + This code for the Arduino Robot's motor board + is the stock firmware. program the motor board with + this sketch whenever you want to return the motor + board to its default state. + +*/ + +#include + +void setup(){ + RobotMotor.begin(); +} +void loop(){ + RobotMotor.parseCommand(); + RobotMotor.process(); +} diff --git a/lineFollow.cpp b/lineFollow.cpp new file mode 100644 index 0000000..d6ebed8 --- /dev/null +++ b/lineFollow.cpp @@ -0,0 +1,152 @@ +//#include +#include "LineFollow.h" + +//#define KP 19 //0.1 units +//#define KD 14 +//#define ROBOT_SPEED 100 //percentage + +//#define KP 11 +//#define KD 5 +//#define ROBOT_SPEED 50 + +//#define INTEGRATION_TIME 10 //En ms + +/*uint8_t KP=11; +uint8_t KD=5; +uint8_t robotSpeed=50; //percentage +uint8_t intergrationTime=10;*/ + +#define NIVEL_PARA_LINEA 50 + +/*int lectura_sensor[5], last_error=0, acu=0; + +//Estos son los arrays que hay que rellenar con los valores de los sensores +//de suelo sobre blanco y negro. +int sensor_blanco[]={ + 0,0,0,0,0}; +int sensor_negro[]={ + 1023,1023,1023,1023,1023}; +*/ +//unsigned long time; + +//void mueve_robot(int vel_izq, int vel_der); +//void para_robot(); +//void doCalibration(int speedPct, int time); +//void ajusta_niveles(); //calibrate values + +LineFollow::LineFollow(){ + /*KP=11; + KD=5; + robotSpeed=50; //percentage + intergrationTime=10;*/ + config(11,5,50,10); + + for(int i=0;i<5;i++){ + sensor_blanco[i]=0; + sensor_negro[i]=1023; + } +} + +void LineFollow::config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime){ + this->KP=KP; + this->KD=KD; + this->robotSpeed=robotSpeed; + this->intergrationTime=intergrationTime; + /*Serial.print("LFC: "); + Serial.print(KP); + Serial.print(' '); + Serial.print(KD); + Serial.print(' '); + Serial.print(robotSpeed); + Serial.print(' '); + Serial.println(intergrationTime);*/ + +} +void LineFollow::calibIRs(){ + static bool isInited=false;//So only init once + if(isInited)return ; + + delay(1000); + + doCalibration(30,500); + doCalibration(-30,800); + doCalibration(30,500); + + delay(1000); + isInited=true; +} + +void LineFollow::runLineFollow(){ + for(int count=0; count<5; count++) + { + lectura_sensor[count]=map(IRread(count),sensor_negro[count],sensor_blanco[count],0,127); + acu+=lectura_sensor[count]; + } + + //Serial.println(millis()); + if (acu > NIVEL_PARA_LINEA) + { + acu/=5; + + int error = ((lectura_sensor[0]<<6)+(lectura_sensor[1]<<5)-(lectura_sensor[3]<<5)-(lectura_sensor[4]<<6))/acu; + + error = constrain(error,-100,100); + + //Calculamos la correcion de velocidad mediante un filtro PD + int vel = (error * KP)/10 + (error-last_error)*KD; + + last_error = error; + + //Corregimos la velocidad de avance con el error de salida del filtro PD + int motor_left = constrain((robotSpeed + vel),-100,100); + int motor_right =constrain((robotSpeed - vel),-100,100); + + //Movemos el robot + //motorsWritePct(motor_left,motor_right); + motorsWritePct(motor_left,motor_right); + + //Esperamos un poquito a que el robot reaccione + delay(intergrationTime); + } + else + { + //Hemos encontrado una linea negra + //perpendicular a nuestro camino + //paramos el robot + motorsStop(); + + //y detenemos la ejecución del programa + //while(true); + reportActionDone(); + //setMode(MODE_SIMPLE); + } +} + + +void LineFollow::doCalibration(int speedPct, int time){ + motorsWritePct(speedPct, -speedPct); + unsigned long beginTime = millis(); + while((millis()-beginTime) sensor_blanco[count]) + sensor_blanco[count]=lectura; + + if (lectura < sensor_negro[count]) + sensor_negro[count]=lectura; + } +} + + + + + + From 59a153a9c7ebb4eda88c90d9910abc08c7aa2518 Mon Sep 17 00:00:00 2001 From: Xun Yang Date: Wed, 21 Aug 2013 23:04:42 +0200 Subject: [PATCH 18/67] Fixed robot libraries and examples for unified Arduino core --- ArduinoRobotMotorBoard.cpp | 12 ++++++++---- ArduinoRobotMotorBoard.h | 1 + LineFollow.h | 2 +- examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino | 2 +- lineFollow.cpp | 4 ++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/ArduinoRobotMotorBoard.cpp b/ArduinoRobotMotorBoard.cpp index 7740a06..93cf43c 100644 --- a/ArduinoRobotMotorBoard.cpp +++ b/ArduinoRobotMotorBoard.cpp @@ -230,16 +230,20 @@ void RobotMotorBoard::_analogRead(uint8_t codename){ messageOut.sendData(); } int RobotMotorBoard::IRread(uint8_t num){ - IRs.selectPin(num-1); //To make consistant with the pins labeled on the board + return _IRread(num-1); //To make consistant with the pins labeled on the board +} + +int RobotMotorBoard::_IRread(uint8_t num){ + IRs.selectPin(num); return IRs.getAnalogValue(); } + void RobotMotorBoard::_readIR(){ - //Serial.println("readIR"); int value; messageOut.writeByte(COMMAND_READ_IR_RE); - for(int i=1;i<6;i++){ - value=IRread(i); + for(int i=0;i<5;i++){ + value=_IRread(i); messageOut.writeInt(value); } messageOut.sendData(); diff --git a/ArduinoRobotMotorBoard.h b/ArduinoRobotMotorBoard.h index c1004c4..2bbc8ea 100644 --- a/ArduinoRobotMotorBoard.h +++ b/ArduinoRobotMotorBoard.h @@ -105,6 +105,7 @@ class RobotMotorBoard:public LineFollow{ void _digitalWrite(uint8_t codename, bool value); void _analogRead(uint8_t codename); void _digitalRead(uint8_t codename); + int _IRread(uint8_t num); void _readIR(); void _readTrim(); diff --git a/LineFollow.h b/LineFollow.h index 608d573..8c5bc49 100644 --- a/LineFollow.h +++ b/LineFollow.h @@ -19,7 +19,7 @@ class LineFollow{ //virtual void motorsWrite(int speedL, int speedR)=0; virtual void motorsWritePct(int speedLpct, int speedRpct)=0; virtual void motorsStop()=0; - virtual int IRread(uint8_t num)=0; + virtual int _IRread(uint8_t num)=0; protected: virtual void reportActionDone()=0; diff --git a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino index e201fd9..160097e 100644 --- a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino +++ b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino @@ -19,7 +19,7 @@ void setup(){ void loop(){ bar=String(""); // empty the string // read the sensors and add them to the string - bar=bar+RobotMotor.readIR(0)+' '+RobotMotor.readIR(1)+' '+RobotMotor.readIR(2)+' '+RobotMotor.readIR(3)+' '+RobotMotor.readIR(4); + bar=bar+RobotMotor.IRread(1)+' '+RobotMotor.IRread(2)+' '+RobotMotor.IRread(3)+' '+RobotMotor.IRread(4)+' '+RobotMotor.IRread(5); // print out the values Serial.println(bar); delay(100); diff --git a/lineFollow.cpp b/lineFollow.cpp index d6ebed8..71eacb5 100644 --- a/lineFollow.cpp +++ b/lineFollow.cpp @@ -79,7 +79,7 @@ void LineFollow::calibIRs(){ void LineFollow::runLineFollow(){ for(int count=0; count<5; count++) { - lectura_sensor[count]=map(IRread(count),sensor_negro[count],sensor_blanco[count],0,127); + lectura_sensor[count]=map(_IRread(count),sensor_negro[count],sensor_blanco[count],0,127); acu+=lectura_sensor[count]; } @@ -135,7 +135,7 @@ void LineFollow::ajusta_niveles() int lectura=0; for(int count=0; count<5; count++){ - lectura=IRread(count); + lectura=_IRread(count); if (lectura > sensor_blanco[count]) sensor_blanco[count]=lectura; From 24d455021cb7abf723c3ac4ac2a597c519d429a2 Mon Sep 17 00:00:00 2001 From: Fede85 Date: Tue, 10 Sep 2013 17:21:47 +0200 Subject: [PATCH 19/67] Robot_Motor library to the 1.5 format --- library.properties | 10 ++++++++++ .../ArduinoRobotMotorBoard.cpp | 0 .../ArduinoRobotMotorBoard.h | 0 EasyTransfer2.cpp => src/EasyTransfer2.cpp | 0 EasyTransfer2.h => src/EasyTransfer2.h | 0 LineFollow.h => src/LineFollow.h | 0 Multiplexer.cpp => src/Multiplexer.cpp | 0 Multiplexer.h => src/Multiplexer.h | 0 lineFollow.cpp => src/lineFollow.cpp | 0 9 files changed, 10 insertions(+) create mode 100644 library.properties rename ArduinoRobotMotorBoard.cpp => src/ArduinoRobotMotorBoard.cpp (100%) rename ArduinoRobotMotorBoard.h => src/ArduinoRobotMotorBoard.h (100%) rename EasyTransfer2.cpp => src/EasyTransfer2.cpp (100%) rename EasyTransfer2.h => src/EasyTransfer2.h (100%) rename LineFollow.h => src/LineFollow.h (100%) rename Multiplexer.cpp => src/Multiplexer.cpp (100%) rename Multiplexer.h => src/Multiplexer.h (100%) rename lineFollow.cpp => src/lineFollow.cpp (100%) diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..014c8dc --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=Robot Motor +author=Arduino +email=info@arduino.cc +sentence=This is the library for programming the Motor Board of the Arduino Robot. +paragraph= +url=http://arduino.cc/en/Reference/RobotLibrary +architectures=avr +version=1.0 +dependencies= +core-dependencies=arduino (>=1.5.0) diff --git a/ArduinoRobotMotorBoard.cpp b/src/ArduinoRobotMotorBoard.cpp similarity index 100% rename from ArduinoRobotMotorBoard.cpp rename to src/ArduinoRobotMotorBoard.cpp diff --git a/ArduinoRobotMotorBoard.h b/src/ArduinoRobotMotorBoard.h similarity index 100% rename from ArduinoRobotMotorBoard.h rename to src/ArduinoRobotMotorBoard.h diff --git a/EasyTransfer2.cpp b/src/EasyTransfer2.cpp similarity index 100% rename from EasyTransfer2.cpp rename to src/EasyTransfer2.cpp diff --git a/EasyTransfer2.h b/src/EasyTransfer2.h similarity index 100% rename from EasyTransfer2.h rename to src/EasyTransfer2.h diff --git a/LineFollow.h b/src/LineFollow.h similarity index 100% rename from LineFollow.h rename to src/LineFollow.h diff --git a/Multiplexer.cpp b/src/Multiplexer.cpp similarity index 100% rename from Multiplexer.cpp rename to src/Multiplexer.cpp diff --git a/Multiplexer.h b/src/Multiplexer.h similarity index 100% rename from Multiplexer.h rename to src/Multiplexer.h diff --git a/lineFollow.cpp b/src/lineFollow.cpp similarity index 100% rename from lineFollow.cpp rename to src/lineFollow.cpp From 9904cb103e9c2283fc3df92dee92c582b4b77e33 Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Mon, 21 Oct 2013 09:58:40 +0200 Subject: [PATCH 20/67] Run new astyle formatter against all the examples --- .../Robot_IR_Array_Test/Robot_IR_Array_Test.ino | 16 ++++++++-------- examples/Robot_Motor_Core/Robot_Motor_Core.ino | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino index 160097e..be19bc6 100644 --- a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino +++ b/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino @@ -1,25 +1,25 @@ /* Motor Board IR Array Test - - This example of the Arduno robot's motor board returns the - values read fron the 5 infrared sendors on the bottom of + + This example of the Arduno robot's motor board returns the + values read fron the 5 infrared sendors on the bottom of the robot. */ -// include the motor board header +// include the motor board header #include String bar; // string for storing the informaton -void setup(){ +void setup() { // start serial communication Serial.begin(9600); // initialize the library RobotMotor.begin(); } -void loop(){ - bar=String(""); // empty the string +void loop() { + bar = String(""); // empty the string // read the sensors and add them to the string - bar=bar+RobotMotor.IRread(1)+' '+RobotMotor.IRread(2)+' '+RobotMotor.IRread(3)+' '+RobotMotor.IRread(4)+' '+RobotMotor.IRread(5); + bar = bar + RobotMotor.IRread(1) + ' ' + RobotMotor.IRread(2) + ' ' + RobotMotor.IRread(3) + ' ' + RobotMotor.IRread(4) + ' ' + RobotMotor.IRread(5); // print out the values Serial.println(bar); delay(100); diff --git a/examples/Robot_Motor_Core/Robot_Motor_Core.ino b/examples/Robot_Motor_Core/Robot_Motor_Core.ino index f74f30a..e7911d8 100644 --- a/examples/Robot_Motor_Core/Robot_Motor_Core.ino +++ b/examples/Robot_Motor_Core/Robot_Motor_Core.ino @@ -1,18 +1,18 @@ /* Motor Core This code for the Arduino Robot's motor board - is the stock firmware. program the motor board with + is the stock firmware. program the motor board with this sketch whenever you want to return the motor board to its default state. - + */ #include -void setup(){ +void setup() { RobotMotor.begin(); } -void loop(){ +void loop() { RobotMotor.parseCommand(); RobotMotor.process(); } From 98a12369280502b605cb8cb166a98442f0271937 Mon Sep 17 00:00:00 2001 From: Xun Yang Date: Wed, 12 Feb 2014 02:02:20 +0100 Subject: [PATCH 21/67] Fixed issue #1478, #1599, #1709, motors being opposite, updated turning algorithm --- ArduinoRobotMotorBoard.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ArduinoRobotMotorBoard.cpp b/ArduinoRobotMotorBoard.cpp index 93cf43c..4d795e0 100644 --- a/ArduinoRobotMotorBoard.cpp +++ b/ArduinoRobotMotorBoard.cpp @@ -170,20 +170,20 @@ void RobotMotorBoard::motorsWrite(int speedL, int speedR){ speedL*=(1-motorAdjustment); } - if(speedL>0){ - analogWrite(IN_A1,speedL); + if(speedR>0){ + analogWrite(IN_A1,speedR); analogWrite(IN_A2,0); }else{ analogWrite(IN_A1,0); - analogWrite(IN_A2,-speedL); + analogWrite(IN_A2,-speedR); } - if(speedR>0){ - analogWrite(IN_B1,speedR); + if(speedL>0){ + analogWrite(IN_B1,speedL); analogWrite(IN_B2,0); }else{ analogWrite(IN_B1,0); - analogWrite(IN_B2,-speedR); + analogWrite(IN_B2,-speedL); } } void RobotMotorBoard::motorsWritePct(int speedLpct, int speedRpct){ From c7b141368c79d6fbfb252c1cb9eee8f27e1a871c Mon Sep 17 00:00:00 2001 From: Victor Tseng Date: Thu, 13 Feb 2014 17:37:34 +0800 Subject: [PATCH 22/67] Use Stream instead of *Serial/TwoWire for newer arduinos, - HardwareSerial - NewSoftSerial - TwoWire - Serial_ (USB CDC) are all subclass of the Stream abstract class. and this way we automagically supports all transfer method which is derivered from Stream. --- EasyTransfer/EasyTransfer.cpp | 28 ++++++++++++++-------------- EasyTransfer/EasyTransfer.h | 6 +++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/EasyTransfer/EasyTransfer.cpp b/EasyTransfer/EasyTransfer.cpp index 4d9d672..f2d56f6 100644 --- a/EasyTransfer/EasyTransfer.cpp +++ b/EasyTransfer/EasyTransfer.cpp @@ -4,10 +4,10 @@ //Captures address and size of struct -void EasyTransfer::begin(uint8_t * ptr, uint8_t length, HardwareSerial *theSerial){ +void EasyTransfer::begin(uint8_t * ptr, uint8_t length, Stream *theStream){ address = ptr; size = length; - _serial = theSerial; + _stream = theStream; //dynamic creation of rx parsing buffer in RAM rx_buffer = (uint8_t*) malloc(size); @@ -16,14 +16,14 @@ void EasyTransfer::begin(uint8_t * ptr, uint8_t length, HardwareSerial *theSeria //Sends out struct in binary, with header, length info and checksum void EasyTransfer::sendData(){ uint8_t CS = size; - _serial->write(0x06); - _serial->write(0x85); - _serial->write(size); + _stream->write(0x06); + _stream->write(0x85); + _stream->write(size); for(int i = 0; iwrite(*(address+i)); + _stream->write(*(address+i)); } - _serial->write(CS); + _stream->write(CS); } @@ -32,17 +32,17 @@ boolean EasyTransfer::receiveData(){ //start off by looking for the header bytes. If they were already found in a previous call, skip it. if(rx_len == 0){ //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. - if(_serial->available() >= 3){ + if(_stream->available() >= 3){ //this will block until a 0x06 is found or buffer size becomes less then 3. - while(_serial->read() != 0x06) { + while(_stream->read() != 0x06) { //This will trash any preamble junk in the serial buffer //but we need to make sure there is enough in the buffer to process while we trash the rest //if the buffer becomes too empty, we will escape and try again on the next call - if(_serial->available() < 3) + if(_stream->available() < 3) return false; } - if (_serial->read() == 0x85){ - rx_len = _serial->read(); + if (_stream->read() == 0x85){ + rx_len = _stream->read(); //make sure the binary structs on both Arduinos are the same size. if(rx_len != size){ rx_len = 0; @@ -54,8 +54,8 @@ boolean EasyTransfer::receiveData(){ //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. if(rx_len != 0){ - while(_serial->available() && rx_array_inx <= rx_len){ - rx_buffer[rx_array_inx++] = _serial->read(); + while(_stream->available() && rx_array_inx <= rx_len){ + rx_buffer[rx_array_inx++] = _stream->read(); } if(rx_len == (rx_array_inx-1)){ diff --git a/EasyTransfer/EasyTransfer.h b/EasyTransfer/EasyTransfer.h index 9562856..d5c3705 100644 --- a/EasyTransfer/EasyTransfer.h +++ b/EasyTransfer/EasyTransfer.h @@ -34,7 +34,7 @@ GNU General Public License for more details. #else #include "WProgram.h" #endif -#include "HardwareSerial.h" +#include "Stream.h" //#include #include #include @@ -43,12 +43,12 @@ GNU General Public License for more details. class EasyTransfer { public: -void begin(uint8_t *, uint8_t, HardwareSerial *theSerial); +void begin(uint8_t *, uint8_t, Stream *theStream); //void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); void sendData(); boolean receiveData(); private: -HardwareSerial *_serial; +Stream *_stream; //NewSoftSerial *_serial; uint8_t * address; //address of struct uint8_t size; //size of struct From fc1379d373fb6414f6f3d93a6af4984fee518674 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 18 Feb 2014 17:22:40 +0100 Subject: [PATCH 23/67] Updated all library.properties to 1.5 rev2 lib format --- library.properties | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library.properties b/library.properties index 014c8dc..5e4395a 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,8 @@ name=Robot Motor +version=1.0 author=Arduino -email=info@arduino.cc +maintainer=Arduino sentence=This is the library for programming the Motor Board of the Arduino Robot. paragraph= url=http://arduino.cc/en/Reference/RobotLibrary architectures=avr -version=1.0 -dependencies= -core-dependencies=arduino (>=1.5.0) From 2a59f60781d770665ed595043319e99df9d3e4d8 Mon Sep 17 00:00:00 2001 From: Fede85 Date: Fri, 18 Jul 2014 19:11:54 +0200 Subject: [PATCH 24/67] modified sentences in library.properties files --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 5e4395a..f96f1c6 100644 --- a/library.properties +++ b/library.properties @@ -2,7 +2,7 @@ name=Robot Motor version=1.0 author=Arduino maintainer=Arduino -sentence=This is the library for programming the Motor Board of the Arduino Robot. +sentence=Enables easy access to the motors of the Arduino Robot Motor board. For Arduino Robot only. paragraph= url=http://arduino.cc/en/Reference/RobotLibrary architectures=avr From b96b7cf7792870fc19af837e68facabb173b2ba9 Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Wed, 27 Aug 2014 22:12:10 -0500 Subject: [PATCH 25/67] Fixed illegal memory access bug. Whoops! Thanks to Joel and Vini for emailing me about it/ --- EasyTransfer/EasyTransfer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EasyTransfer/EasyTransfer.cpp b/EasyTransfer/EasyTransfer.cpp index f2d56f6..aec45da 100644 --- a/EasyTransfer/EasyTransfer.cpp +++ b/EasyTransfer/EasyTransfer.cpp @@ -10,7 +10,7 @@ void EasyTransfer::begin(uint8_t * ptr, uint8_t length, Stream *theStream){ _stream = theStream; //dynamic creation of rx parsing buffer in RAM - rx_buffer = (uint8_t*) malloc(size); + rx_buffer = (uint8_t*) malloc(size+1); } //Sends out struct in binary, with header, length info and checksum @@ -84,4 +84,4 @@ boolean EasyTransfer::receiveData(){ } return false; -} \ No newline at end of file +} From 5b4760b88f99d3e27e1d59651e68034f025b8d68 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 29 Aug 2014 15:16:19 +0200 Subject: [PATCH 26/67] Fixed some libraries metadata. --- library.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/library.properties b/library.properties index f96f1c6..326233f 100644 --- a/library.properties +++ b/library.properties @@ -4,5 +4,6 @@ author=Arduino maintainer=Arduino sentence=Enables easy access to the motors of the Arduino Robot Motor board. For Arduino Robot only. paragraph= +category=Device Control url=http://arduino.cc/en/Reference/RobotLibrary architectures=avr From cb6b46fa251341627acf5db832f19f25f106a371 Mon Sep 17 00:00:00 2001 From: Arturo Guadalupi Date: Fri, 16 Jan 2015 15:35:22 +0100 Subject: [PATCH 27/67] Added README.adoc for the library manager project --- README.adoc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 README.adoc diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..07bad3d --- /dev/null +++ b/README.adoc @@ -0,0 +1,24 @@ += Robot Motor Library for Arduino = + +The Robot has a number of built in sensors and actuators. The library is designed to easily access the robot's functionality. + +For more information about this library please visit us at +http://arduino.cc/en/Reference/RobotLibrary + +== License == + +Copyright (c) Arduino LLC. All right 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. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA From 820a6f124610edb38e2f01348d337817a1076db4 Mon Sep 17 00:00:00 2001 From: Bill Porter Date: Mon, 16 Feb 2015 16:07:19 -0600 Subject: [PATCH 28/67] Removed unused header file references. Don't remember why we referenced so many header files. Shouldn't need to anymore. --- EasyTransfer/EasyTransfer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/EasyTransfer/EasyTransfer.h b/EasyTransfer/EasyTransfer.h index d5c3705..25f8546 100644 --- a/EasyTransfer/EasyTransfer.h +++ b/EasyTransfer/EasyTransfer.h @@ -36,10 +36,10 @@ GNU General Public License for more details. #endif #include "Stream.h" //#include -#include -#include -#include -#include +//#include +//#include +//#include +//#include class EasyTransfer { public: @@ -60,4 +60,4 @@ uint8_t calc_CS; //calculated Chacksum -#endif \ No newline at end of file +#endif From 158348c996d134ea4bcb41f75c827671d7913573 Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Fri, 27 Mar 2015 15:01:49 +0100 Subject: [PATCH 29/67] Libraries: version now compliant with semver. See http://semver.org/ --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 326233f..6e95f86 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Robot Motor -version=1.0 +version=1.0.0 author=Arduino maintainer=Arduino sentence=Enables easy access to the motors of the Arduino Robot Motor board. For Arduino Robot only. From 132bad021035f7851ba7c63ec601da6890fb484e Mon Sep 17 00:00:00 2001 From: Arturo Guadalupi Date: Fri, 10 Apr 2015 11:26:04 +0200 Subject: [PATCH 30/67] minor changes --- src/EasyTransfer2.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/EasyTransfer2.cpp b/src/EasyTransfer2.cpp index 24427cc..bf17678 100644 --- a/src/EasyTransfer2.cpp +++ b/src/EasyTransfer2.cpp @@ -1,3 +1,27 @@ +/****************************************************************** +* EasyTransfer Arduino Library +* details and example sketch: +* http://www.billporter.info/easytransfer-arduino-library/ +* +* Brought to you by: +* Bill Porter +* www.billporter.info +* +* See Readme for other info and version history +* +* +*This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +* +*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. +*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or +*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +******************************************************************/ + #include "EasyTransfer2.h" @@ -149,4 +173,4 @@ boolean EasyTransfer2::receiveData(){ //Serial.print(" "); //Serial.println("Short"); return false; -} \ No newline at end of file +} From 7640e30977332138847bac0c44ae54c78a60540d Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Thu, 23 Apr 2015 13:01:30 +0200 Subject: [PATCH 31/67] Bundled libraries update --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 6e95f86..165412a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Robot Motor -version=1.0.0 +version=1.0.1 author=Arduino maintainer=Arduino sentence=Enables easy access to the motors of the Arduino Robot Motor board. For Arduino Robot only. From 42816c773e00872f7ba2dc498d496fc335475e59 Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Wed, 20 May 2015 17:10:06 +0200 Subject: [PATCH 32/67] Due to website configuration changes, every url starting with http://arduino.cc has been changed to http://www.arduino.cc. Fixes #3191 --- README.adoc | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 07bad3d..480fa52 100644 --- a/README.adoc +++ b/README.adoc @@ -3,7 +3,7 @@ The Robot has a number of built in sensors and actuators. The library is designed to easily access the robot's functionality. For more information about this library please visit us at -http://arduino.cc/en/Reference/RobotLibrary +http://www.arduino.cc/en/Reference/RobotLibrary == License == diff --git a/library.properties b/library.properties index 165412a..bbfc60c 100644 --- a/library.properties +++ b/library.properties @@ -5,5 +5,5 @@ maintainer=Arduino sentence=Enables easy access to the motors of the Arduino Robot Motor board. For Arduino Robot only. paragraph= category=Device Control -url=http://arduino.cc/en/Reference/RobotLibrary +url=http://www.arduino.cc/en/Reference/RobotLibrary architectures=avr From fa74e2625dd26cd27923bc541bce603da5f8a24d Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Wed, 10 Jun 2015 15:00:43 +0200 Subject: [PATCH 33/67] Robot_Motor: releasing version 1.0.2 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index bbfc60c..7e697df 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Robot Motor -version=1.0.1 +version=1.0.2 author=Arduino maintainer=Arduino sentence=Enables easy access to the motors of the Arduino Robot Motor board. For Arduino Robot only. From 29bd4eee59f9a8aae5f40e069989a02de468f157 Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Fri, 25 Sep 2015 15:16:45 +0200 Subject: [PATCH 34/67] Fixed license headers. See https://github.com/arduino/Arduino/pull/3498 --- src/ArduinoRobotMotorBoard.cpp | 20 +++++++++++++++++++- src/ArduinoRobotMotorBoard.h | 20 +++++++++++++++++++- src/LineFollow.h | 20 +++++++++++++++++++- src/Multiplexer.cpp | 20 +++++++++++++++++++- src/Multiplexer.h | 18 ++++++++++++++++++ src/lineFollow.cpp | 18 ++++++++++++++++++ 6 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/ArduinoRobotMotorBoard.cpp b/src/ArduinoRobotMotorBoard.cpp index 4d795e0..1a68b77 100644 --- a/src/ArduinoRobotMotorBoard.cpp +++ b/src/ArduinoRobotMotorBoard.cpp @@ -1,3 +1,21 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + #include "ArduinoRobotMotorBoard.h" #include "EasyTransfer2.h" #include "Multiplexer.h" @@ -266,4 +284,4 @@ void RobotMotorBoard::reportActionDone(){ messageOut.sendData(); } -RobotMotorBoard RobotMotor=RobotMotorBoard(); \ No newline at end of file +RobotMotorBoard RobotMotor=RobotMotorBoard(); diff --git a/src/ArduinoRobotMotorBoard.h b/src/ArduinoRobotMotorBoard.h index 2bbc8ea..1d3a713 100644 --- a/src/ArduinoRobotMotorBoard.h +++ b/src/ArduinoRobotMotorBoard.h @@ -1,3 +1,21 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + #ifndef ArduinoRobot_h #define ArduinoRobot_h @@ -123,4 +141,4 @@ class RobotMotorBoard:public LineFollow{ extern RobotMotorBoard RobotMotor; -#endif \ No newline at end of file +#endif diff --git a/src/LineFollow.h b/src/LineFollow.h index 8c5bc49..0ae0e4c 100644 --- a/src/LineFollow.h +++ b/src/LineFollow.h @@ -1,3 +1,21 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + #ifndef LINE_FOLLOW_H #define LINE_FOLLOW_H @@ -37,4 +55,4 @@ class LineFollow{ int sensor_negro[5]; }; -#endif \ No newline at end of file +#endif diff --git a/src/Multiplexer.cpp b/src/Multiplexer.cpp index c0fdd86..71a6d05 100644 --- a/src/Multiplexer.cpp +++ b/src/Multiplexer.cpp @@ -1,3 +1,21 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + #include "Multiplexer.h" void Multiplexer::begin(uint8_t* selectors, uint8_t Z, uint8_t length){ @@ -34,4 +52,4 @@ int Multiplexer::getAnalogValueAt(uint8_t num){ bool Multiplexer::getDigitalValueAt(uint8_t num){ selectPin(num); return getDigitalValue(); -} \ No newline at end of file +} diff --git a/src/Multiplexer.h b/src/Multiplexer.h index a0c4c5c..fcff1f8 100644 --- a/src/Multiplexer.h +++ b/src/Multiplexer.h @@ -1,3 +1,21 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + #ifndef Multiplexer_h #define Multiplexer_h diff --git a/src/lineFollow.cpp b/src/lineFollow.cpp index 71eacb5..08964e9 100644 --- a/src/lineFollow.cpp +++ b/src/lineFollow.cpp @@ -1,3 +1,21 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + //#include #include "LineFollow.h" From e7190eaccab9eb630c12a9813d03da85de85d5ff Mon Sep 17 00:00:00 2001 From: PaulStoffregen Date: Wed, 30 Nov 2016 13:56:25 -0800 Subject: [PATCH 35/67] Use fixed 16 bit integers for compatibility between boards --- .../EasyTransfer_2Way_wPot_Example.pde | 6 +++--- .../EasyTransfer_2Way_wServo_Example.pde | 6 +++--- .../EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde | 4 ++-- .../EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde | 4 ++-- .../EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde | 4 ++-- .../EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde | 4 ++-- .../ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde | 4 ++-- .../ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde | 4 ++-- .../SoftEasyTransfer_RX_Example.pde | 4 ++-- .../SoftEasyTransfer_TX_Example.pde | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) rename EasyTransfer/{Examples => examples}/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde (97%) rename EasyTransfer/{Examples => examples}/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde (97%) rename EasyTransfer/{Examples => examples}/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde (96%) rename EasyTransfer/{Examples => examples}/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde (96%) rename EasyTransferI2C/{Examples => examples}/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde (96%) rename EasyTransferI2C/{Examples => examples}/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde (96%) rename EasyTransferVirtualWire/{Examples => examples}/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde (97%) rename EasyTransferVirtualWire/{Examples => examples}/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde (96%) rename SoftEasyTransfer/{Examples => examples}/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde (97%) rename SoftEasyTransfer/{Examples => examples}/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde (97%) diff --git a/EasyTransfer/Examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde b/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde similarity index 97% rename from EasyTransfer/Examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde rename to EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde index 9c02885..e47fc21 100644 --- a/EasyTransfer/Examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde +++ b/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde @@ -24,14 +24,14 @@ EasyTransfer ETin, ETout; struct RECEIVE_DATA_STRUCTURE{ //put your variable definitions here for the data you want to receive //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int buttonstate; + int16_t buttonstate; }; struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to receive //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int buttonstate; - int servoval; + int16_t buttonstate; + int16_t servoval; }; //give a name to the group of data diff --git a/EasyTransfer/Examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde b/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde similarity index 97% rename from EasyTransfer/Examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde rename to EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde index 22dc9e1..f5f2a16 100644 --- a/EasyTransfer/Examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde +++ b/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde @@ -24,14 +24,14 @@ Servo myservo; struct RECEIVE_DATA_STRUCTURE{ //put your variable definitions here for the data you want to receive //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int buttonstate; - int servoval; + int16_t buttonstate; + int16_t servoval; }; struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to receive //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int buttonstate; + int16_t buttonstate; }; diff --git a/EasyTransfer/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde similarity index 96% rename from EasyTransfer/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde rename to EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde index 166e1cd..0a25b87 100644 --- a/EasyTransfer/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde +++ b/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde @@ -6,8 +6,8 @@ EasyTransfer ET; struct RECEIVE_DATA_STRUCTURE{ //put your variable definitions here for the data you want to receive //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data diff --git a/EasyTransfer/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde b/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde similarity index 96% rename from EasyTransfer/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde rename to EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde index 96489a4..8221cb2 100644 --- a/EasyTransfer/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde +++ b/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde @@ -6,8 +6,8 @@ EasyTransfer ET; struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to send //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data diff --git a/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/EasyTransferI2C/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde similarity index 96% rename from EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde rename to EasyTransferI2C/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde index bf8ec4f..9cb63ee 100644 --- a/EasyTransferI2C/Examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde +++ b/EasyTransferI2C/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde @@ -7,8 +7,8 @@ EasyTransferI2C ET; struct RECEIVE_DATA_STRUCTURE{ //put your variable definitions here for the data you want to receive //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data diff --git a/EasyTransferI2C/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde b/EasyTransferI2C/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde similarity index 96% rename from EasyTransferI2C/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde rename to EasyTransferI2C/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde index 1422a99..0faa119 100644 --- a/EasyTransferI2C/Examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde +++ b/EasyTransferI2C/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde @@ -7,8 +7,8 @@ EasyTransferI2C ET; struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to send //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data diff --git a/EasyTransferVirtualWire/Examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde b/EasyTransferVirtualWire/examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde similarity index 97% rename from EasyTransferVirtualWire/Examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde rename to EasyTransferVirtualWire/examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde index eba62a9..7368361 100644 --- a/EasyTransferVirtualWire/Examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde +++ b/EasyTransferVirtualWire/examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde @@ -9,8 +9,8 @@ struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to send //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO //Struct can'e be bigger then 26 bytes for VirtualWire version - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data diff --git a/EasyTransferVirtualWire/Examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde b/EasyTransferVirtualWire/examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde similarity index 96% rename from EasyTransferVirtualWire/Examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde rename to EasyTransferVirtualWire/examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde index d42f1ee..e8c2c27 100644 --- a/EasyTransferVirtualWire/Examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde +++ b/EasyTransferVirtualWire/examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde @@ -9,8 +9,8 @@ struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to send //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO //Struct can'e be bigger then 26 bytes for VirtualWire version - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data diff --git a/SoftEasyTransfer/Examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde b/SoftEasyTransfer/examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde similarity index 97% rename from SoftEasyTransfer/Examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde rename to SoftEasyTransfer/examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde index 70edbe2..e5ae83c 100644 --- a/SoftEasyTransfer/Examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde +++ b/SoftEasyTransfer/examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde @@ -15,8 +15,8 @@ SoftEasyTransfer ET; struct RECEIVE_DATA_STRUCTURE{ //put your variable definitions here for the data you want to receive //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data diff --git a/SoftEasyTransfer/Examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde b/SoftEasyTransfer/examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde similarity index 97% rename from SoftEasyTransfer/Examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde rename to SoftEasyTransfer/examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde index 07aecff..70fa85b 100644 --- a/SoftEasyTransfer/Examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde +++ b/SoftEasyTransfer/examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde @@ -16,8 +16,8 @@ SoftEasyTransfer ET; struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to send //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int blinks; - int pause; + int16_t blinks; + int16_t pause; }; //give a name to the group of data From 06f6d696f3c8c7d726043af9581a3a959c7a3e2e Mon Sep 17 00:00:00 2001 From: PaulStoffregen Date: Wed, 30 Nov 2016 14:04:35 -0800 Subject: [PATCH 36/67] Use INPUT_PULLUP, for better compatibility with non-AVR boards --- .../EasyTransfer_2Way_wPot_Example.pde | 3 +-- .../EasyTransfer_2Way_wServo_Example.pde | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde b/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde index e47fc21..8b36ddc 100644 --- a/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde +++ b/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde @@ -46,9 +46,8 @@ void setup(){ ETout.begin(details(txdata), &Serial); pinMode(13, OUTPUT); - pinMode(12, INPUT); //enable pull-up - digitalWrite(12, HIGH); + pinMode(12, INPUT_PULLUP); } diff --git a/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde b/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde index f5f2a16..832ac6b 100644 --- a/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde +++ b/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde @@ -47,9 +47,8 @@ void setup(){ ETout.begin(details(txdata), &Serial); pinMode(13, OUTPUT); - pinMode(12, INPUT); //enable pull-up - digitalWrite(12, HIGH); + pinMode(12, INPUT_PULLUP); myservo.attach(9); } From 4ee643702a2af1fa802f765682afac2477ba70f0 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Thu, 19 Jan 2017 08:54:18 +0100 Subject: [PATCH 37/67] Changed board.txt with USB VID/PID settings according to https://github.com/sparkfun/Arduino_Boards/blob/master/sparkfun/avr/boards.txt --- avr/boards.txt | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/avr/boards.txt b/avr/boards.txt index ca7409e..f87b876 100644 --- a/avr/boards.txt +++ b/avr/boards.txt @@ -29,24 +29,23 @@ arduinorobot.bootloader.lock_bits=0x2F arduinorobot.build.mcu=atmega32u4 arduinorobot.build.f_cpu=16000000L arduinorobot.build.core=arduino:arduino +arduinorobot.build.vid=0x2341 arduinorobot.build.extra_flags={build.usb_flags} ## Arduino Robot Control Board ## ------------------------------------------------- arduinorobot.menu.cpu.controlboard=Control Board -arduinorobot.menu.cpu.controlboard.vid.0=0x2341 -arduinorobot.menu.cpu.controlboard.pid.0=0x0038 -arduinorobot.menu.cpu.controlboard.vid.1=0x2341 -arduinorobot.menu.cpu.controlboard.pid.1=0x8038 -arduinorobot.menu.cpu.controlboard.vid.2=0x2A03 -arduinorobot.menu.cpu.controlboard.pid.2=0x0038 -arduinorobot.menu.cpu.controlboard.vid.3=0x2A03 -arduinorobot.menu.cpu.controlboard.pid.3=0x8038 - arduinorobot.menu.cpu.controlboard.bootloader.file=caterina-Arduino_Robot/Caterina-Robot-Control.hex -arduinorobot.menu.cpu.controlboard.build.vid=0x2341 +arduinorobot.menu.cpu.controlboard.build.vid.0=0x2341 +arduinorobot.menu.cpu.controlboard.build.pid.0=0x0038 +arduinorobot.menu.cpu.controlboard.build.vid.1=0x2341 +arduinorobot.menu.cpu.controlboard.build.pid.1=0x8038 +arduinorobot.menu.cpu.controlboard.build.vid.2=0x2A03 +arduinorobot.menu.cpu.controlboard.build.pid.2=0x0038 +arduinorobot.menu.cpu.controlboard.build.vid.3=0x2A03 +arduinorobot.menu.cpu.controlboard.build.pid.3=0x8038 arduinorobot.menu.cpu.controlboard.build.pid=0x8038 arduinorobot.menu.cpu.controlboard.build.usb_product="Robot Control" arduinorobot.menu.cpu.controlboard.build.board=AVR_ROBOT_CONTROL @@ -56,18 +55,16 @@ arduinorobot.menu.cpu.controlboard.build.variant=arduino:robot_control ## ------------------------------------------------- arduinorobot.menu.cpu.motorboard=Motor Board -arduinorobot.menu.cpu.motorboard.vid.0=0x2341 -arduinorobot.menu.cpu.motorboard.pid.0=0x0039 -arduinorobot.menu.cpu.motorboard.vid.1=0x2341 -arduinorobot.menu.cpu.motorboard.pid.1=0x8039 -arduinorobot.menu.cpu.motorboard.vid.2=0x2A03 -arduinorobot.menu.cpu.motorboard.pid.2=0x0039 -arduinorobot.menu.cpu.motorboard.vid.3=0x2A03 -arduinorobot.menu.cpu.motorboard.pid.3=0x8039 - arduinorobot.menu.cpu.motorboard.bootloader.file=caterina-Arduino_Robot/Caterina-Robot-Motor.hex -arduinorobot.menu.cpu.motorboard.build.vid=0x2341 +arduinorobot.menu.cpu.motorboard.build.vid.0=0x2341 +arduinorobot.menu.cpu.motorboard.build.pid.0=0x0039 +arduinorobot.menu.cpu.motorboard.build.vid.1=0x2341 +arduinorobot.menu.cpu.motorboard.build.pid.1=0x8039 +arduinorobot.menu.cpu.motorboard.build.vid.2=0x2A03 +arduinorobot.menu.cpu.motorboard.build.pid.2=0x0039 +arduinorobot.menu.cpu.motorboard.build.vid.3=0x2A03 +arduinorobot.menu.cpu.motorboard.build.pid.3=0x8039 arduinorobot.menu.cpu.motorboard.build.pid=0x8039 arduinorobot.menu.cpu.motorboard.build.usb_product="Robot Motor" arduinorobot.menu.cpu.motorboard.build.board=AVR_ROBOT_MOTOR From d2ad0dddbe48caf52afaae05114b28d56d04306b Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sun, 5 Feb 2017 16:01:57 +0100 Subject: [PATCH 38/67] Added AnalogMultiplexer library --- .../AnalogMultiplexerExample.ino | 48 ++++++ avr/libraries/AnalogMultiplexer/keywords.txt | 26 ++++ .../AnalogMultiplexer/library.properties | 9 ++ .../src/AnalogMultiplexer.cpp | 44 ++++++ .../AnalogMultiplexer/src/AnalogMultiplexer.h | 141 ++++++++++++++++++ 5 files changed, 268 insertions(+) create mode 100644 avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino create mode 100644 avr/libraries/AnalogMultiplexer/keywords.txt create mode 100644 avr/libraries/AnalogMultiplexer/library.properties create mode 100644 avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp create mode 100644 avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h diff --git a/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino b/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino new file mode 100644 index 0000000..9a972b4 --- /dev/null +++ b/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino @@ -0,0 +1,48 @@ +#include + +// Selected Arduino pins. +enum { + PIN_EN = 2, // The enable pin of the multiplexer. + PIN_S0 = 3, // Channel selector pin 0. + PIN_S1 = 4, // Channel selector pin 1. + PIN_S2 = 5, // Channel selector pin 2. + PIN_S3 = 6, // Channel selector pin 3. + PIN_IO = A0 // Use here a pin that can work in analog & digital modes. +}; + +AMxx4067 mux{ PIN_EN, PIN_S0, PIN_S1, PIN_S2, PIN_S3 }; // 16:1 Multiplexer. +// Other possible types are: +//AMxx4051 mux{ PIN_EN, PIN_S0, PIN_S1, PIN_S2 }; // 8:1 Multiplexer. +//AMxx4052 mux{ PIN_EN, PIN_S0, PIN_S1 }; // Dual 4:1 Multiplexer. +//AMxx4053 mux{ PIN_EN, PIN_S0 }; // Triple 2:1 Multiplexer. + +void setup() { + Serial.begin(9600); + while (!Serial); + + mux.pinMode(PIN_IO, INPUT); // PIN_IO will be used to read from the mux. + mux.enable(); // Enable multiplexer. + for (int channel = 0; channel < 16; channel++) { + int value = mux.analogRead(channel); // Do analog read. + Serial.println(value); + } + Serial.println(); + + mux.pinMode(PIN_IO, INPUT_PULLUP); // Enable weak pull up resistor. + for (int channel = 0; channel < 16; channel++) { + int value = mux.digitalRead(channel); // Do digital read. + Serial.println(value); + } + Serial.println(); + + mux.pinMode(PIN_IO, OUTPUT); // Switch to write mode. + for (int channel = 0; channel < 16; channel++) { + int value = channel % 2; + mux.digitalWrite(channel, value); // Do digital write (multiplexed). + Serial.println(value); + } + Serial.println(); + mux.disable(); // Switch of multiplexer. +} + +void loop() { } diff --git a/avr/libraries/AnalogMultiplexer/keywords.txt b/avr/libraries/AnalogMultiplexer/keywords.txt new file mode 100644 index 0000000..d19998a --- /dev/null +++ b/avr/libraries/AnalogMultiplexer/keywords.txt @@ -0,0 +1,26 @@ +####################################### +# Syntax Coloring Map for +# AnalogMultiplexer +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +AnalogMultiplexer KEYWORD1 +AMxx40xx KEYWORD1 +AMxx4051 KEYWORD1 +AMxx4052 KEYWORD1 +AMxx4053 KEYWORD1 +AMxx4067 KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +enable KEYWORD2 +disable KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### \ No newline at end of file diff --git a/avr/libraries/AnalogMultiplexer/library.properties b/avr/libraries/AnalogMultiplexer/library.properties new file mode 100644 index 0000000..227b950 --- /dev/null +++ b/avr/libraries/AnalogMultiplexer/library.properties @@ -0,0 +1,9 @@ +name=AnalogMultiplexer +version=1.0.0 +author=Julian Sanin +maintainer=Julian Sanin +sentence=A library to interface with analog multiplexers. +paragraph=Supports 4051, 4052, 4053, and 4067 series multiplexers. +category=Uncategorized +url=https://github.com/j54n1n/arduinorobot +architectures=avr diff --git a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp new file mode 100644 index 0000000..3ec2d02 --- /dev/null +++ b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp @@ -0,0 +1,44 @@ +#include "AnalogMultiplexer.h" + +AnalogMultiplexer::AnalogMultiplexer(uint8_t pinEnable) { + _pinEnable = pinEnable; + _pinCommon = NOT_A_PIN; + /*Arduino*/::pinMode(_pinEnable, OUTPUT); +} + +void AnalogMultiplexer::enable() { + /*Arduino*/::digitalWrite(_pinEnable, LOW); +} + +void AnalogMultiplexer::disable() { + /*Arduino*/::digitalWrite(_pinEnable, HIGH); +} + +void AnalogMultiplexer::pinMode(uint8_t pinCommon, uint8_t mode) { + if (NOT_A_PIN != digitalPinToPort(pinCommon)) { + _pinCommon = pinCommon; + /*Arduino*/::pinMode(_pinCommon, mode); + } else { + _pinCommon = NOT_A_PIN; + } +} + +void AnalogMultiplexer::digitalWrite(uint8_t channel, uint8_t value) { + if ((NOT_A_PIN != _pinCommon) && (NOT_A_PIN != selectChannel(channel))) { + /*Arduino*/::digitalWrite(_pinCommon, value); + } +} + +int AnalogMultiplexer::digitalRead(uint8_t channel) { + if ((NOT_A_PIN != _pinCommon) && (NOT_A_PIN != selectChannel(channel))) { + return /*Arduino*/::digitalRead(_pinCommon); + } + return LOW; +} + +int AnalogMultiplexer::analogRead(uint8_t channel) { + if ((NOT_A_PIN != _pinCommon) && (NOT_A_PIN != selectChannel(channel))) { + return /*Arduino*/::analogRead(_pinCommon); + } + return 0; +} diff --git a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h new file mode 100644 index 0000000..4825908 --- /dev/null +++ b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h @@ -0,0 +1,141 @@ +#ifndef ANALOG_MULTIPLEXER_H +#define ANALOG_MULTIPLEXER_H + +#include + +/// CMOS Logic Analog Multiplexer and Demultiplexer. +class AnalogMultiplexer { + + /// Channel enable. + uint8_t _pinEnable; + + /// Channel common input or output. + uint8_t _pinCommon; + +protected: + /// + /// Select channel (from 0 to MAX) to be connected to common pin. + /// + virtual uint8_t selectChannel(uint8_t channel) = 0; + +public: + explicit AnalogMultiplexer(uint8_t pinEnable); + + /// Enables the analog multiplexer channels. + void enable(); + + /// Disables the analog multiplexer channels. + void disable(); + + /// + /// Configures the specified common channel pin to behave either as an input + /// or an output. + /// + /// + /// The pin number of whose mode you wish to set. The selected pin will be + /// used for , + /// , and + /// . + /// + /// + /// , , or + /// . + /// + void pinMode(uint8_t pinCommon, uint8_t mode); + + /// + /// Write a or a value to a specified + /// common channel pin. + /// + /// + /// + /// The channel number. + /// + /// + /// or . + /// + void digitalWrite(uint8_t channel, uint8_t value); + + /// + /// Reads the value from a specified common channel pin, either + /// or . + /// + /// + /// The channel number of the multiplexer pin you want to read. + /// + /// + /// or . + /// + int digitalRead(uint8_t channel); + + /// + /// Reads the value from the specified common channel pin. + /// + /// + /// The channel number of the multiplexer pin you want to read. + /// + /// + /// 0 to 1023. + /// + int analogRead(uint8_t channel); +}; + +/// +/// Generic class for all types of analog mulitplexer with selector pins. +/// +template +class AMxx40xx : public AnalogMultiplexer { + + enum { MAX_SELECTORS = sizeof...(Args) }; + + /// 2^n selectors for each channel. + uint8_t _pinSelectors[MAX_SELECTORS]; + + /// + /// See . + /// + uint8_t selectChannel(uint8_t channel) { + if ((1 << MAX_SELECTORS) > channel) { + for (uint8_t i = 0; i < MAX_SELECTORS; i++) { + const uint8_t pinSelector = _pinSelectors[i]; + /*Arduino*/::digitalWrite(pinSelector, bitRead(channel, i)); + } + return channel + 1; + } + return NOT_A_PIN; + } + +public: + /// + /// Setup an analog multiplexer with given enable pin and selector pins. + /// + AMxx40xx(uint8_t pinEnable, Args... pinSelector) + : AnalogMultiplexer(pinEnable), _pinSelectors{pinSelector...} { + for (uint8_t i = 0; i < MAX_SELECTORS; i++) { + /*Arduino*/::pinMode(_pinSelectors[i], OUTPUT); + } + } +}; + +/// +/// 4051 Series Single 8:1 Analog Multiplexer. +/// +using AMxx4051 = AMxx40xx; + +/// +/// 4052 Series Dual 4:1 Analog Multiplexer. +/// +using AMxx4052 = AMxx40xx; + +/// +/// 4053 Series Triple 2:1 Analog Multiplexer. Assuming all 3 selectors are tied +/// together. +/// +using AMxx4053 = AMxx40xx; + +/// +/// 4067 Series Single 16:1 Analog Multiplexer. +/// +using AMxx4067 = AMxx40xx; + +#endif //ANALOG_MULTIPLEXER From 73dbf0bce6c2edbcbea97a382bb5912e6d5f2196 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Tue, 7 Feb 2017 14:13:07 +0100 Subject: [PATCH 39/67] Preparing merge of EasyTransfer library --- EasyTransfer/README.txt | 61 --------- EasyTransferI2C/EasyTransferI2C.cpp | 115 ----------------- EasyTransferI2C/EasyTransferI2C.h | 65 ---------- .../EasyTransfer_RX_Example.pde | 45 ------- .../EasyTransfer_TX_Example.pde | 47 ------- EasyTransferI2C/keywords.txt | 22 ---- .../EasyTransferVirtualWire.cpp | 74 ----------- .../EasyTransferVirtualWire.h | 64 ---------- .../ETVirtualWireDemoRX.pde | 51 -------- .../ETVirtualWireDemoTX.pde | 50 -------- EasyTransferVirtualWire/keywords.txt | 22 ---- I2C_Wiring.png | Bin 44906 -> 0 bytes SoftEasyTransfer/SoftEasyTransfer.cpp | 118 ------------------ SoftEasyTransfer/SoftEasyTransfer.h | 77 ------------ .../SoftEasyTransfer_RX_Example.pde | 48 ------- .../SoftEasyTransfer_TX_Example.pde | 53 -------- SoftEasyTransfer/keywords.txt | 22 ---- UARTS_Wiring.png | Bin 44889 -> 0 bytes .../libraries/EasyTransfer}/EasyTransfer.cpp | 0 .../libraries/EasyTransfer}/EasyTransfer.h | 0 .../libraries/EasyTransfer}/I2C_Wiring.png | Bin .../libraries/EasyTransfer/README.txt | 0 .../libraries/EasyTransfer}/UARTS_Wiring.png | Bin .../EasyTransfer_2Way_wPot_Example.pde | 0 .../EasyTransfer_2Way_wServo_Example.pde | 0 .../EasyTransfer_RX_Example.pde | 0 .../EasyTransfer_TX_Example.pde | 0 .../libraries/EasyTransfer}/keywords.txt | 0 28 files changed, 934 deletions(-) delete mode 100644 EasyTransfer/README.txt delete mode 100644 EasyTransferI2C/EasyTransferI2C.cpp delete mode 100644 EasyTransferI2C/EasyTransferI2C.h delete mode 100644 EasyTransferI2C/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde delete mode 100644 EasyTransferI2C/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde delete mode 100644 EasyTransferI2C/keywords.txt delete mode 100644 EasyTransferVirtualWire/EasyTransferVirtualWire.cpp delete mode 100644 EasyTransferVirtualWire/EasyTransferVirtualWire.h delete mode 100644 EasyTransferVirtualWire/examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde delete mode 100644 EasyTransferVirtualWire/examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde delete mode 100644 EasyTransferVirtualWire/keywords.txt delete mode 100644 I2C_Wiring.png delete mode 100644 SoftEasyTransfer/SoftEasyTransfer.cpp delete mode 100644 SoftEasyTransfer/SoftEasyTransfer.h delete mode 100644 SoftEasyTransfer/examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde delete mode 100644 SoftEasyTransfer/examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde delete mode 100644 SoftEasyTransfer/keywords.txt delete mode 100644 UARTS_Wiring.png rename {EasyTransfer => avr/libraries/EasyTransfer}/EasyTransfer.cpp (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/EasyTransfer.h (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/I2C_Wiring.png (100%) rename README.txt => avr/libraries/EasyTransfer/README.txt (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/UARTS_Wiring.png (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde (100%) rename {EasyTransfer => avr/libraries/EasyTransfer}/keywords.txt (100%) diff --git a/EasyTransfer/README.txt b/EasyTransfer/README.txt deleted file mode 100644 index ac89911..0000000 --- a/EasyTransfer/README.txt +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************** -* EasyTransfer Arduino Library v1.7 -* details and example sketch: -* http://www.billporter.info/easytransfer-arduino-library/ -* -* Brought to you by: -* Bill Porter -* www.billporter.info -* -* Lib version history -* 1.0 Created -* 1.1 Fixed dumb Copy-paste error in header file -* Added a keyword file -* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a better way -* added passing in of Serial port of different types -* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum failed, -* I'm good at dumb mistakes -* 1.7 Fixed a bug where the receive function could block for too long and never process data correctly -* Organized the examples to be Arduino IDE compatible -* 1.8 -* Now Arduino 1.0 compatible! -* -* -* Limits of the Library -* You can change the Serial port, -* but the Struct size must not pass 255 bytes -* -* The protcol is as follows: -* Header(0x06,0x85),SizeofPayload,Payload,Checksum -* -* -*This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ - - -********************To Install************************************* - -To install, unzip and place 'EasyTransfer' folder into your 'C:\Users\{user name}\Documents\Arduino\libraries' folder or '{Arduino IDE path}\hardware\libraries" or {Arduino IDE path}\libraries" directory. - -Restart the Arduino IDE, look for the Library under "Sketch" -> "Import Library". You can also try the examples by finding them -under "File" -> "Examples" -> "EasyTransfer". - -All uses of the library are in the example sketchs. - - -******************************************************************* - - -Library now has two versions, one for regular hardware Serial, one for use with the NewSoftSerial library -making any Arduino pin capable of transfering data back and forth easily. - -See the examples to find out how to use the library. \ No newline at end of file diff --git a/EasyTransferI2C/EasyTransferI2C.cpp b/EasyTransferI2C/EasyTransferI2C.cpp deleted file mode 100644 index fd7a190..0000000 --- a/EasyTransferI2C/EasyTransferI2C.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "EasyTransferI2C.h" - - - - -//Captures address and size of struct -void EasyTransferI2C::begin(uint8_t * ptr, uint8_t length, TwoWire *theSerial){ - address = ptr; - size = length; - _serial = theSerial; - - //dynamic creation of rx parsing buffer in RAM - rx_buffer = (uint8_t*) malloc(size); -} - -//Sends out struct in binary, with header, length info and checksum -void EasyTransferI2C::sendData(uint8_t i2c_address){ - uint8_t CS = size; - _serial->beginTransmission(i2c_address); -#if ARDUINO >= 100 - _serial->write(0x06); - _serial->write(0x85); - _serial->write(size); -#else - _serial->send(0x06); - _serial->send(0x85); - _serial->send(size); -#endif - for(int i = 0; i= 100 - _serial->write(*(address+i)); -#else - _serial->send(*(address+i)); -#endif - } -#if ARDUINO >= 100 - _serial->write(CS); -#else - _serial->send(CS); -#endif - _serial->endTransmission(); -} - -boolean EasyTransferI2C::receiveData(){ - - //start off by looking for the header bytes. If they were already found in a previous call, skip it. - if(rx_len == 0){ - //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. - if(_serial->available() >= 3){ - //this will block until a 0x06 is found or buffer size becomes less then 3. -#if ARDUINO >= 100 - while(_serial->read() != 0x06) { -#else - while(_serial->receive() != 0x06) { -#endif - //This will trash any preamble junk in the serial buffer - //but we need to make sure there is enough in the buffer to process while we trash the rest - //if the buffer becomes too empty, we will escape and try again on the next call - if(_serial->available() < 3) - return false; - } -#if ARDUINO >= 100 - if (_serial->read() == 0x85){ - rx_len = _serial->read(); -#else - if (_serial->receive() == 0x85){ - rx_len = _serial->receive(); -#endif - //make sure the binary structs on both Arduinos are the same size. - if(rx_len != size){ - rx_len = 0; - return false; - } - } - } - } - - //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. - if(rx_len != 0){ - while(_serial->available() && rx_array_inx <= rx_len){ -#if ARDUINO >= 100 - rx_buffer[rx_array_inx++] = _serial->read(); -#else - rx_buffer[rx_array_inx++] = _serial->receive(); -#endif - } - - if(rx_len == (rx_array_inx-1)){ - //seem to have got whole message - //last uint8_t is CS - calc_CS = rx_len; - for (int i = 0; i -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ -#ifndef EasyTransferI2C_h -#define EasyTransferI2C_h - - -//make it a little prettier on the front end. -#define details(name) (byte*)&name,sizeof(name) - -//Not neccessary, but just in case. -#if ARDUINO > 22 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif -#include "HardwareSerial.h" -//#include -#include -#include -#include -#include -#include - -class EasyTransferI2C { -public: -void begin(uint8_t *, uint8_t, TwoWire *theSerial); -void sendData(uint8_t address); -boolean receiveData(); -private: -TwoWire *_serial; -//NewSoftSerial *_serial; -uint8_t * address; //address of struct -uint8_t size; //size of struct -uint8_t * rx_buffer; //address for temporary storage and parsing buffer -uint8_t rx_array_inx; //index for RX parsing buffer -uint8_t rx_len; //RX packet length according to the packet -uint8_t calc_CS; //calculated Chacksum -}; - - - -#endif diff --git a/EasyTransferI2C/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/EasyTransferI2C/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde deleted file mode 100644 index 9cb63ee..0000000 --- a/EasyTransferI2C/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include - -//create object -EasyTransferI2C ET; - -struct RECEIVE_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to receive - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -RECEIVE_DATA_STRUCTURE mydata; - -//define slave i2c address -#define I2C_SLAVE_ADDRESS 9 - -void setup(){ - Wire.begin(I2C_SLAVE_ADDRESS); - //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. - ET.begin(details(mydata), &Wire); - //define handler function on receiving data - Wire.onReceive(receive); - - pinMode(13, OUTPUT); - -} - -void loop() { - //check and see if a data packet has come in. - if(ET.receiveData()){ - //this is how you access the variables. [name of the group].[variable name] - //since we have data, we will blink it out. - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - } -} - -void receive(int numBytes) {} diff --git a/EasyTransferI2C/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde b/EasyTransferI2C/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde deleted file mode 100644 index 0faa119..0000000 --- a/EasyTransferI2C/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include - -//create object -EasyTransferI2C ET; - -struct SEND_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to send - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -SEND_DATA_STRUCTURE mydata; - -//define slave i2c address -#define I2C_SLAVE_ADDRESS 9 - -void setup(){ - Wire.begin(); - //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. - ET.begin(details(mydata), &Wire); - - pinMode(13, OUTPUT); - - randomSeed(analogRead(0)); - -} - -void loop(){ - //this is how you access the variables. [name of the group].[variable name] - mydata.blinks = random(5); - mydata.pause = random(5); - //send the data - ET.sendData(I2C_SLAVE_ADDRESS); - - //Just for fun, we will blink it out too - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - - delay(5000); -} diff --git a/EasyTransferI2C/keywords.txt b/EasyTransferI2C/keywords.txt deleted file mode 100644 index 8e83157..0000000 --- a/EasyTransferI2C/keywords.txt +++ /dev/null @@ -1,22 +0,0 @@ -####################################### -# Syntax Coloring Map EasyTransfer -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -EasyTransferI2C KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### -sendData KEYWORD2 -receiveData KEYWORD2 -begin KEYWORD2 - - -####################################### -# Constants (LITERAL1) -####################################### -details LITERAL1 diff --git a/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp b/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp deleted file mode 100644 index e56a941..0000000 --- a/EasyTransferVirtualWire/EasyTransferVirtualWire.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "EasyTransferVirtualWire.h" -#include - -//so to make this easy, I'm just making my library be another data -// layer on top of Virtual wire. - - -//Captures address and size of struct -void EasyTransferVirtualWire::begin(uint8_t * ptr, uint8_t length) { //HardwareSerial *theSerial){ -address = ptr; -size = length; -//_serial = theSerial; -} - -//Sends out struct in binary, with header, length info and checksum -void EasyTransferVirtualWire::sendData(){ - - //temp storage place - uint8_t temp_buffer[size+4]; - - uint8_t CS = size; - temp_buffer[0] = 0x06; - temp_buffer[1] = 0x85; - temp_buffer[2] = size; - - for(int i = 0; i -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ -#ifndef EasyTransferVirtualWire_h -#define EasyTransferVirtualWire_h - - -//make it a little prettier on the front end. -#define details(name) (byte*)&name,sizeof(name) - -//Not neccessary, but just in case. -#if ARDUINO > 22 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif -#include -//#include "HardwareSerial.h" -//#include -#include -#include -#include -#include - -class EasyTransferVirtualWire { -public: -void begin(uint8_t *, uint8_t); -//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); -void sendData(); -boolean receiveData(); -private: -//HardwareSerial *_serial; -//NewSoftSerial *_serial; -uint8_t * address; //address of struct -uint8_t size; //size of struct -uint8_t rx_len; //RX packet length according to the packet -uint8_t rx_array[255]; //RX packet parsing buffer -uint8_t rx_array_inx; //index for RX parsing buffer -uint8_t calc_CS; //calculated Chacksum -}; - - - -#endif \ No newline at end of file diff --git a/EasyTransferVirtualWire/examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde b/EasyTransferVirtualWire/examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde deleted file mode 100644 index 7368361..0000000 --- a/EasyTransferVirtualWire/examples/ETVirtualWireDemoRX/ETVirtualWireDemoRX.pde +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include - - -//create object -EasyTransferVirtualWire ET; - -struct SEND_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to send - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - //Struct can'e be bigger then 26 bytes for VirtualWire version - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -SEND_DATA_STRUCTURE mydata; - -void setup(){ - //start the library, pass in the data details - ET.begin(details(mydata)); - - // Initialise the IO and ISR - vw_set_ptt_inverted(true); // Required for DR3100 - vw_setup(2000); // Bits per sec - - vw_rx_start(); // Start the receiver PLL running - - pinMode(13, OUTPUT); - - randomSeed(analogRead(0)); - -} - -void loop(){ - //check and see if a data packet has come in. - if(ET.receiveData()){ - //this is how you access the variables. [name of the group].[variable name] - //since we have data, we will blink it out. - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - } - - //you should make this delay shorter then your transmit delay or else messages could be lost - delay(250); -} - diff --git a/EasyTransferVirtualWire/examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde b/EasyTransferVirtualWire/examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde deleted file mode 100644 index e8c2c27..0000000 --- a/EasyTransferVirtualWire/examples/ETVirtualWireDemoTX/ETVirtualWireDemoTX.pde +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include - - -//create object -EasyTransferVirtualWire ET; - -struct SEND_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to send - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - //Struct can'e be bigger then 26 bytes for VirtualWire version - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -SEND_DATA_STRUCTURE mydata; - -void setup(){ - //start the library, pass in the data details - ET.begin(details(mydata)); - - // Initialise the IO and ISR - vw_set_ptt_inverted(true); // Required for DR3100 - vw_setup(2000); // Bits per sec - - pinMode(13, OUTPUT); - - randomSeed(analogRead(0)); - -} - -void loop(){ - //this is how you access the variables. [name of the group].[variable name] - mydata.blinks = random(5); - mydata.pause = random(5); - //send the data - ET.sendData(); - - //Just for fun, we will blink it out too - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - - delay(5000); -} - diff --git a/EasyTransferVirtualWire/keywords.txt b/EasyTransferVirtualWire/keywords.txt deleted file mode 100644 index 49c101b..0000000 --- a/EasyTransferVirtualWire/keywords.txt +++ /dev/null @@ -1,22 +0,0 @@ -####################################### -# Syntax Coloring Map EasyTransfer -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -EasyTransferVirtualWire KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### -sendData KEYWORD2 -receiveData KEYWORD2 -begin KEYWORD2 - - -####################################### -# Constants (LITERAL1) -####################################### -details LITERAL1 diff --git a/I2C_Wiring.png b/I2C_Wiring.png deleted file mode 100644 index 4c15cd736d3ed5443768179aa453d63e40c84c18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44906 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 m8!O2GDg9&xL}~;3{y>zu4ArR)r)NR|e$ 22 -//Captures address and size of struct -void SoftEasyTransfer::begin(uint8_t * ptr, uint8_t length, SoftwareSerial *theSerial){ - address = ptr; - size = length; - _serial = theSerial; - - //dynamic creation of rx parsing buffer in RAM - rx_buffer = (uint8_t*) malloc(size); -} - -#else -//Captures address and size of struct -void SoftEasyTransfer::begin(uint8_t * ptr, uint8_t length, NewSoftSerial *theSerial){ - address = ptr; - size = length; - _serial = theSerial; - - //dynamic creation of rx parsing buffer in RAM - rx_buffer = (uint8_t*) malloc(size); -} - -#endif - -#if ARDUINO > 22 -//Sends out struct in binary, with header, length info and checksum -void SoftEasyTransfer::sendData(){ - uint8_t CS = size; - _serial->write(0x06); - _serial->write(0x85); - _serial->write(size); - for(int i = 0; iwrite(*(address+i)); - } - _serial->write(CS); - -} -#else -//Sends out struct in binary, with header, length info and checksum -void SoftEasyTransfer::sendData(){ - uint8_t CS = size; - _serial->print(0x06, BYTE); - _serial->print(0x85, BYTE); - _serial->print(size, BYTE); - for(int i = 0; iprint(*(address+i), BYTE); - } - _serial->print(CS, BYTE); - -} -#endif - -boolean SoftEasyTransfer::receiveData(){ - - //start off by looking for the header bytes. If they were already found in a previous call, skip it. - if(rx_len == 0){ - //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. - if(_serial->available() >= 3){ - //this will block until a 0x06 is found or buffer size becomes less then 3. - while(_serial->read() != 0x06) { - //This will trash any preamble junk in the serial buffer - //but we need to make sure there is enough in the buffer to process while we trash the rest - //if the buffer becomes too empty, we will escape and try again on the next call - if(_serial->available() < 3) - return false; - } - if (_serial->read() == 0x85){ - rx_len = _serial->read(); - //make sure the binary structs on both Arduinos are the same size. - if(rx_len != size){ - rx_len = 0; - return false; - } - } - } - } - - - - if(rx_len != 0){ - while(_serial->available() && rx_array_inx <= rx_len){ - rx_buffer[rx_array_inx++] = _serial->read(); - } - - if(rx_len == (rx_array_inx-1)){ - //seem to have got whole message - //last uint8_t is CS - calc_CS = rx_len; - for (int i = 0; i -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ -#ifndef SoftEasyTransfer_h -#define SoftEasyTransfer_h - - -//make it a little prettier on the front end. -#define details(name) (byte*)&name,sizeof(name) - -//Not neccessary, but just in case. -#if ARDUINO > 22 -#include "Arduino.h" -#include -#else -#include "WProgram.h" -#include -#endif -//#include "HardwareSerial.h" - - -#include -#include -#include -#include - -class SoftEasyTransfer { -public: -//void begin(uint8_t *, uint8_t, HardwareSerial *theSerial); -#if ARDUINO > 22 -void begin(uint8_t *, uint8_t, SoftwareSerial *theSerial); -#else -void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); -#endif -void sendData(); -boolean receiveData(); -private: -//HardwareSerial *_serial; - -#if ARDUINO > 22 -SoftwareSerial *_serial; -#else -NewSoftSerial *_serial; -#endif - - -uint8_t * address; //address of struct -uint8_t size; //size of struct -uint8_t * rx_buffer; //address for temporary storage and parsing buffer -uint8_t rx_array_inx; //index for RX parsing buffer -uint8_t rx_len; //RX packet length according to the packet -uint8_t calc_CS; //calculated Chacksum -}; - - - -#endif \ No newline at end of file diff --git a/SoftEasyTransfer/examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde b/SoftEasyTransfer/examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde deleted file mode 100644 index e5ae83c..0000000 --- a/SoftEasyTransfer/examples/SoftEasyTransfer_RX_Example/SoftEasyTransfer_RX_Example.pde +++ /dev/null @@ -1,48 +0,0 @@ -#include - -/* For Arduino 1.0 and newer, do this: */ -#include -SoftwareSerial mySerial(2, 3); - -/* For Arduino 22 and older, do this: */ -//#include -//NewSoftSerial mySerial(2, 3); - - -//create object -SoftEasyTransfer ET; - -struct RECEIVE_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to receive - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -RECEIVE_DATA_STRUCTURE mydata; - -void setup(){ - mySerial.begin(9600); - //start the library, pass in the data details and the name of the serial port. - ET.begin(details(mydata), &mySerial); - - pinMode(13, OUTPUT); - -} - -void loop(){ - //check and see if a data packet has come in. - if(ET.receiveData()){ - //this is how you access the variables. [name of the group].[variable name] - //since we have data, we will blink it out. - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - } - //you should make this delay shorter then your transmit delay or else messages could be lost - delay(250); -} diff --git a/SoftEasyTransfer/examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde b/SoftEasyTransfer/examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde deleted file mode 100644 index 70fa85b..0000000 --- a/SoftEasyTransfer/examples/SoftEasyTransfer_TX_Example/SoftEasyTransfer_TX_Example.pde +++ /dev/null @@ -1,53 +0,0 @@ -#include - -/* For Arduino 1.0 and newer, do this: */ -#include -SoftwareSerial mySerial(2, 3); - -/* For Arduino 22 and older, do this: */ -//#include -//NewSoftSerial mySerial(2, 3); - - - -//create object -SoftEasyTransfer ET; - -struct SEND_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to send - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -SEND_DATA_STRUCTURE mydata; - -void setup(){ - mySerial.begin(9600); - //start the library, pass in the data details and the name of the serial port. - ET.begin(details(mydata), &mySerial); - - pinMode(13, OUTPUT); - - randomSeed(analogRead(0)); - -} - -void loop(){ - //this is how you access the variables. [name of the group].[variable name] - mydata.blinks = random(5); - mydata.pause = random(5); - //send the data - ET.sendData(); - - //Just for fun, we will blink it out too - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - - delay(5000); -} diff --git a/SoftEasyTransfer/keywords.txt b/SoftEasyTransfer/keywords.txt deleted file mode 100644 index 3b2ecb4..0000000 --- a/SoftEasyTransfer/keywords.txt +++ /dev/null @@ -1,22 +0,0 @@ -####################################### -# Syntax Coloring Map EasyTransfer -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -SoftEasyTransfer KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### -sendData KEYWORD2 -receiveData KEYWORD2 -begin KEYWORD2 - - -####################################### -# Constants (LITERAL1) -####################################### -details LITERAL1 diff --git a/UARTS_Wiring.png b/UARTS_Wiring.png deleted file mode 100644 index 48ba613a928ec1aa0b7f02093d56810078ae53c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44889 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
@3@c~ZpF*CstMCQeZpME#4&M54U0;P zv1W8;ZcF^+MI)ny%;V6kJo(HiBsC@nPR4n;%-il!RldVpxC~{4;eAp7wfWv=6ao~H zMGT-IcU*RCE6yxX0lsXs!M>dA&u;tODP-?(PJ0N9NxD2 zFUFhjJsf{AzWrUr7IL8nHVOLwb{hC@f&Ag*>B-=te~Rku_;l^F;!8ze6tsRFZqi1a zK#^~iESgXfyckLRg?`H-+8Cc`l2Hz;` zxNHj=V1J5?xa#~;fICg3BiavGQ({bjLr1B-U{5qguAW%{CO6kBG>*$ONazC6%a znVO3ItGfQV0t=Coh7BR=dU0kt-4TQUypE_0ymn?(AX5WOyvQt9pnTAAV|((Y{b#h= zyc(vm-9!NipYhY9YDcZW0}b*KnX4O3U0@smVT&u*b{E)qI9_FJ#AS5zG5%IOCN6Zq zxO?liAF~>G0aFxGGQmkl1&x_EFJx)I$A*h-y#<4+8he9>{nupIky9Rp=ig=(6{_~u zhPhqQDXM!2Jq?XNW7lPa3N705Uxj19F>PGJnJd_{N@?~zgmAywYqEioUEEP< zvRR?i{TBGUS>Yy%^@;V_-zC9;Vu3U_z9uhyOkim?Cv(;FjXWu;6nttcB8$XgQGoyQY$4~$Jsl|2lw5>L7M z4R32yW$JbPADX@bD2^`JIyeMLaQN`x?ry=|-QC?C0t63EaM$3zI0OimMT4`rySx6w zd+)EJsM;-dXPA3$_vt>TyBV)4`np7oNdJP;RpFGA5^vVCWkZz_b5|*6mVnUqO;STS@N>;`_N^cGPVZaQ$%uljh?2ST%neT zBg>yk6#)Xco&M((fIyZ6(7PmM*;-g+M?3)8n0KkmvE4x_2m8+wi-%+(B>H)PTfId_afDlyl_NiH5HBLyAp z5Svma^{HlME|3>$EK)7mJ3x6ipPRwz^&}OXv>Mc3Tye~|(#yOGNmF*#j)jl&yuO&J zDMFHKPu&O*0Z1g44Ytj_T4?gd4Qot5%4wR)PclZ2pww{XZl!O>a4`6#_+sX?2)JFL zBSD=H92!xXD!a|vUj&Kb7e%8XVO2IXy`;cl=ETD?%9Jzs^Nzl2TpaA)ZTjU8JH5(Y zQq6e&!H=?oOF)uum@GEoO9Z%O>vKJ*>~~L~J1P*lJP#2-blx^gTW==-Vx-k-gdy;L z5Uu-V=x#Ic%rV1gOxUm`JFl}j1~84DBHFY;-s@bt0PktN75CjwuIpi-V{v}zUBt(^ z6RSH0|FhbtEe-c}{%V-t@BW~4W%}~fL#NM_ezHbgRkM)!Z6soe-D9!)k z$axUmEo*9^VfJ0TU&*0IvX-=O7$0I2C z{r03ewk0>wjqT+C6C?8ScAVqouRq%i%9}2&@M;@2a6N+~Ao!uc+Jz}A-CF!eMMj1s z)BRn}RqP`-_l+us^e3{g)!uKcc*`uOU4JDU8)i9gn$F_!V>#xeGZQH*y@h+$z6tQL zTkY;=2ds9jHqURWH4YYKQT6D6gm#>*yqqug$+SUZIqX9X_eM_C%e&DR{WgfN8w**2m}U6~U9HeS~t8&6KoMhM9e?AO^AN4`etLIckY`)^CIP+LlsT z#o5WOk5BZPce|~E=SvYk{!Z*Zqrxl#}f=`VuO*9WyxJk>u$?1OAP{5kV2C-HW(|L{f8 zsB4@*(~1ZZjeuOjpkWPaxT;U$KI5ng8|SAk85t(#OUu33<0qyKv>1Bl;c}grIBS}& z1#bTKKlR%XB-ZH;Zs^PY4*5a*cc-9b?)+t5lf}h2u$CvBtbaSAw${ESP$5?MPpDH> z;a@LHn5EuBzQFUPkLqo9kEYTfB6QmNjC%AteqLXg21+Cu8oXYe(vIqrD436}F0%OS zve7bLda>q*T1X)$Z z%@P?!UP6*ByWB%{`wUm z6f(;l2E@_sm}PD|MGxPs_0n}Da(1VH{`f1m!-BDCpCCkV*9yCd8+ zI`Z^$taM+U1TMy!(^U57Jz%0Nz|g?O0THDIELTOC-rg5fz>=ZlvjC0jXCrX!rVccO7ciO28XY=UcSU~_7=|C3x=M}c$M_l*xar9}P6 zG-BC0Et#5M=z>TeXGqwhun--Y;)}UjrDYwtB*pBw1TM3Tg;@t~X2}8PVgHF}tt!v3 zC#G!v%+@Pw$B(|f3MFu1W5146i_M=>SE#EidgxA|u}MzosyfKG#LlV}6l)JjuZT9q zlLoC+*mY2jq(?71$*v;%3Ts*Eq*#w}uLK`N=;}b|I|gFGgt-j&507 zH$uezX4s$fS<9xME_gWR2zdsbQmeN*(sJf0^9(=?3_6mmTZr6jHNe( zR1cpRtz!&ZIyHbyyf4GNryPF$(1kQm3{;&ADz~m&vHv0v@CS=FEHhD|j+PCQYT9SM{+YR$3`mfXha|X5hRo@@uEDB!uj+yV1PO1wHTiOrezW6o$6-V{ZW z6EBf)8%N3idfILjB{Neg~)WkQZYT7;cPla?K~}Pq3`C6!w#$6(+DSI zX;csDezt?e>t5%z-I6}Yps{(y!4T3OKMy)RN;)HU^)PaQW zW|G)fmA-PfZZX$-9IP3|jsH@R5MdpQY!1*ZvzJS)x4@@d=C#v9Ovh=3hH@{%R7>T* z-c|>TBj3gOLyKu3wJuW}SV!!%)~x#qJ&`Ju&xP9ZzPJ@fQ!NZ00D$Ej^b1hZrH5gik$m>W!=`w4u{9!DdA zO3V66SNRog54J;5y>Wla3Sxgv4=dmEnZ-mP_(PmC9%EI@lXG}T>kAb6v|y}~;t%hu zGp25`+vVM}RqUrAaGu)BOLAU!QqQsv?*|=6t2`d!*Sc}WApZ>Rmb94UqKL#o`qAa! z54qoZa->Lg-^#r7mrbN>_u30=b83d^_In6_RhUf`O*HJ8D;(Jsb$73JpJlBbHyWL$ zY?4hJ^N@MO+L7+)-qqIa)4l14Rz)cuNwn|~r5Frg6&B7JQK zi-@tv`!-eiOiw7pG9~yDJ#3?oZNOx3>Q^wy$AQ zE1tg~5brncSyXR+0Uz`5Uch)ENzGgb`Z#d;POH`=om@OEoj9*(o%?hF-lRP0R+B7; ze!HyA=K^`2y@sdO#~-8RcTIl^_Oqclfe+)L7o?{vv#bv%j2U$FhKqF4<<|Sd+sO z-LvL_J+DYOuq^`T|CA(+J6AiNHyEin2m5fNfZS^JI+J2__d>txd=_Fu-j&;cUVHt&)Da(H&#ew6uGKTsriH z^+TH>w^4`ne?1*1s)3#_VF1LWaeNXEa7D-cnR*>{1#WOS^?Ma`DL_)iWZ7UjrKolQ z4h}tO)yZ_5pRD<^&(+oZVD1r}-P4(u!3c?P8x2>+3GxiQsQ%MEa@B&DxVZ5na$0GY zya)G${-EWl+4%4KZb7NT!F6tqf7n_4t|-Jp6V zk7kaTZ(Ir?OKU@(nbve;pG=^|g&Xd`u)D>Yi(8?5_u1%~9Eb7owxscXzCDKnwm)Ut zylz{L`(pPNrnA-NlLWpYaSzZ9pvjiLNBcjU&pvl+YsYGP>8-Wb78J)!x*Oyr+Ls2L zC{tgXlB=GNHJ<(CC=gDNCproIUZ3|{XRqHtfv5X-Mn1l@YVZ<&Dec?dglV46gs-Ev z{^5Oe+afXCJj?4=MNEJF&0=M1q7{L4pw<9M!59Vg9?kIR&M%ZtT#jUzJzP~V0!V$7 z6I1d}I|HtTw~l}lo82ekz3IK!y@%KiAD3z3_YgszSpD}>cYEvXOZwoUo7 ze}W%?WsDq3bt3KG*>0%s*A7_6%lvW$CScwKoWR3Uhz&r%^qVD_mbH4LotArn5QR!!5)-iQ`Yj_jKdxnU;aA$-f*gN6y&4T2(m zxk9ZyfnqZ{-oq^=d)r-`#Sg~6B|Pkm&B0O{tv<5&Z@MkXLDi66YOA=|7pDH5^_VLv zHz0a@UT?;(S_t}{XAndN#6k~;jb{y8-qrYn3R7ltJY%c%sPp1^e@x?jS17G`U;`~K9$!w<-Vac&RceF%9hsqlKRMiiqa^N* zr);NqT;5nv4=Ci!kc%Ef@?mA;tE}ACKUOgx(--0pMXlrdW})G~p+jxvsy0;y19!FQ z{RVrn@hC|cql%5KU#7SJq9pTQ9^R`5dK|mxISOi7rt2q~uuYk@9O5M&coq&9CWPnT zt*350z8et9s*}7=^iCEIYkHq|SD|w{Tv4No#zS1Cw``_MmMU`=Ov?6Z683`Fd2*%;y4P=Y zO@^iI64yHe3Vy-TQvXUi&|H7D=f3$okq_eRxH5!Ek7930)d5R}PwSU5$L<{PbV_^;Ojf%=Y{MVz|Pvw_z3xQS9Z(fT& zKZYkt|Kb{p=pgkIdqW#ZmvvUe9Z~;uJ^7=860{!0&bM3nH@@LS8#|9T=u14)+UK@> zLbQe)MRgs^Jm$#L$xTBw!?PkFYW@a^Wydm|PE2lX9;Wn`iW7+{;uQ#j?Dr&}@e)6I zNo*Hk!l34S(ui!V+tJ`ymV-Rb{wR3goOv@f;WdzQkPXiy`YKJEfCVaSqbmbZ0K`Yf(20Mg^Ei89jy zPBiw~kKO1TnV(lt!mn=P?#YBak7u8i@r9WG zig<B3JCB=y$IjXpju6r2esfl7rymb7k16 z82i@cD5O-oHZmA3>zMiP_1|83D6)^o1**3$Vzq=>gv*)R+2x*w-ar^74|zDR8h@D} z8&)}2L|-*C&opSnm7jkW|G)J(d^4Zzt{Wh6@gvMa%=9K!a@)dXD@7G;p2PWShV8Z0 z6`AB%aP%c@!qVM@iR85Mt3y5EV`{mD`a?)P>s4pLzz^E-ory{i5Pme~SACEFL_`hQ z+JvNyZjYVpFmxM#EVa0}@|tUSJx3x<+74QyWXIP^k&|$M>CtXa?V`5Rd5Gn2J@~}z z@waa+r~;>@)ODv7o%N`TvZvh!3fL+?5!SS^{Tx>cFgqW1vb-H2uPIcOU2uRc#Do26u`aSW zcEf@K?py539l0nqEV&X7&Fkb)V)zS{sBC8unwKH$$5!B7{waJ!E@lFESm{oA%gJEc z3_O+000~I19nV^3D268U%&ypJNW7?wTdwE}6N}Myp-IAHT;z&*yWw(jWFFcVmvSS2 zObT&l9y(-5AEe}Cd@bEPC>ur;7XtfXg`xK4)6 z8PM0-+;O~KZ#Z#Me*W1Rq7X}kj1<<3`FL(QzCK)_o!Xl??3MZ5KZYFqpda6P)el(edfxuA@dgm4X84)#pxQ7{h-jqr|@A*LL$;XH-%)N_OVIgJ-d65TG`qf2ajp z4K0qe_si%i;_RU^?na^dZB?E8{ONK0t7@kZiJY})?v~l9lLiOXOP0&6%W`z-)!a}a zyE(wl5$4^6}(h#MHnWk!Y${fjN2cho;hJ3Wck93 zY$Uyxc*m`*o34;4W_A;7=I=Y_o%?J}pou~M$uW;Lyv37ny==-_ZW_sdV8P_wsz}*x zCBKP=kHlo%UT#(HUH-PCPd8NkBT*!~fMxK|1faM)LqDiD{T7&((#y6h>Ol#+qz!EY za=_AFZrSZ#G`0wvj-LmQ5_5g46?}5cs`Uwg1P1zSNj&?^;{2^VqyeJrIh&$~Q!oj| z@+-BRX5(>X-d8(Txu?~C9?!3tZO1%1U<8Et1ely zaXD}~DQyg0U?O8L_tqT&hFzmW#_ldI2ls;j0Ewi#+4eaxq5M+ugq>Skz<#>WbE&Df^HA z1kKxXQn0t*1B-$zoSr9VOm0P5v$+J*Qo-vMz=O$ornsGj9Qd0qSsN}B%Ag|h64sAw z1O_1e;1pwI*~}DXmwm2@Y}Nj<1(D^Dkl9y{CnK zKB(bqBn%WB!V~LtlNFGY!paNh|3SZqZ~n!EpVyLG4p+Ge^6{NtfgO`>GD|kvEomP{ zbp{-i|IR_lvY;RIl^nv=_;~1h_5|L_>ht7H@gKQq)QgK>Mk|++Pm>Tkn(^y*_*R>P zGxz-H2EO0W*to`fi)MFZ}zHb7=Z9AsG2D%?f;!MFYA1b7%bz1o( zuBjl{6{xlR)ZMoAf%7#o;EF`4yY~(!XGr*M%QkMF+w}#Ld9ZIXW=~-dKJc!0aKUbE zp*ydjEu-@~>?f}ey%1dB&FR7E`@>rTj zXaw$9G0#ejiB2bI>jSrN3yKuYXaWcKgnv`4m9GD8+7T8Aj>2_D+TC)bogX4Fw_}p= zcmLRPI#CwhyyT(_Y^jEccnJp3xV0jcMmMQmT<&%r$=P}ebAmC*5}j?Vm0~;9edyAw zw@spi?BZM|R-W^}Lt$R1^vS_L6}%M-e<1wiTtL zYWr(9aMP5)<7>Ee@f%wzi^(XBvEZ7L@8D{HWGPm@|F%lcvE0a=unuyZsLTW(~$L6R7O?R|-_TmOj>? zlu3$R-?i_qS6uWzjQJhm7wfugQsS5T$!k8CJ7x)GmIW0CPtOQ))-58`^qO{p$-p1vZ*oWpI~9B< z=1$$DR6Y!Ij}~!~KhX*(ozv-)JovEHjFHdm7h$y~}AH+ceeNeuI&XsQv$104ZC7`&D|9Kd=M9=O|8_ zg}tkJRq6&weo)Tkj63NerrFoV{ActT5)Q;$>-E;Arw7M;Dr1FGxlw+nPqo$SPg4P0 z_{>iB%L4=iUd_HQxO?*J!sA)LmvKDsGrP}L6Hs#Y`|k!pFP-%cLC0&1X*O?ben*%Q zS&Ug)pyQog+G?vb0R1jHx2o!Ehp-RA+xpd^j4o{IhwoW5m`m|yObMBj99OT8StHl8wy@N!;6#Z9q0qc^=Q zZDl9)!i@FJUBL7A%>A{?&Zw#QbDqn!YHFf{9N}^lwEJqg$b`U-uwcE`!YT+0B)Y z$5A(r3$?59BNQ?14dKUl`=OO>5J_zz%StFkZ}qg~)xX`Y>WZOTt3bCcXi`9^zTsOR~e#)r!=wM5D&AOnIgO!`SC1TbBsi%ro=<9Ohhth;l-pqa)y3B3`+ z;Tx8s(AhV?ln;J5I}sWM5W1XUW>!{N3agoQ<7|6DVG;q=PgUK7F`s_;^8=kNH6bb> zMdy-Vk^97)r(n_^p)~g2&~Rs!CqY?veSomnC?Ns8g9`BXoS0JQk_5Fr=dnNiPeOK= z1;8Mm^%c}-+bMZVl*nt0zgMmI5LDdr)nK!#nqRQ?-CS-QYu)BlABNncR(kJYPUfer z5zB^%$%=R*rl3KMZ=3p>c=qW`^D(nd=^ijJQC zI+&V2Dq%Ci45@6SKnwpuZ8^+>bO;ZSn{j#i?D3okaTm{nE1fNSEIr*eP(W^h`q$l? zY-Zjg; zAoiY1Q%x`~*J%OQXAIn*>py5cbeB^c3MJ2i)sVH=gt&zo9=2}>7N&fz|Lf^XY9F3R zhTpmRS(bKr12>()*P4KeO>MfR0^rkfEZA3|5DweJWO}1TBXE^rr_+7MvgYT*`=mGveSREn@Plb|128{k^HFr~#3H=7goK6}>8&Bu%6f^l zPc)U`V*95c?IF;{oN(7Ws@`48YYPvZz0rT^*ej1Q^m<2s2o*;U^Fw1(M?nFd$w|j3 zsjF_tXq2kMX8`KB>RpZAe|1OG|@@IvT9CWr}#%>&+lU+ow7zz2G1;<5_o%>w3}=9ehP83P_BTG@E@ z&6f%2P=OL36#k&~f0`b~#EZ_tsVnxd0;=Vz4zkZ1aT3SnRhG}+{7-!>X#r+ZD~_ah zZ#-|wLVjr{RVOWOgUc+BEv@YQh3WR#( z&!(hB|J4kdppl~I&VJ+^a1nFmY;hcI<*A&01GWUJY-JBZ`?i0o% zYoxZhE;|E+(OktBhoD_xHDeJw^q3SE+fRp2U&k#Py`Sh4vBfQ!8-Da$UaQ{bR>k_A zS{sn(@iWe&e-EPodPavh8p|o0q*6aD{*x(c`y)2Vg18U=9`HCZWcT(S`o6Z>vg^fV zZ(7Y84RhxOon;SCw+m(}KlPQa0P<-|2QPCkCqdUFt0H=Vv)3iHxPeZ*l)kyRLyufxL|yVnx>Wfl_6 zchuqJHF}S(D47#rUqZ-u@lQZ@pAp+Jz~P)i8JzX*oj>-)NP#}qpG(R+T>fFw%Jiw4 zV~J7pUt0lZFma{u_)$Sk7E&$B06Y$$Z}vD{l0{i=93x?I;6xfzdf$^@R;3H*7>V+; z``d3=vazTVI(Mw2pNs@jJdyTMzv_Ta_--Gqc*MWDm{IGjjG&_xX@7bAx8`%WbyCG7 z9F5;adn{nci1wE^()9L6Gm(4yBqw9Jg94oVK+OCaqG*4%T6R=3BY^tw^E1%DM1f%KR2$u+0NB-D!5b2vsk zXLEw+3}3au$I&l`Y%zbJu(Ht`EX?R=tLPd;8V0s2daiaJ)V#|iZp^F*P%V-%SU%_` znRM6mC7F-nRP;C4qtLcdpiNU5)<JwE(O-?8>V=X*v zKbOLc;*%iT_cWu-fZ0NcaQQCYllA)dJfM=F(J+&Sw=VY^KV}cL9HFvEnKBAy?L-76 zy*r09vfa~iL5X)Tdn3p%>{}uu@)%-~U7V+qvlS1+>mw2X?PX%pN>8x+_i<_$)iwGu z=L-I7u6Nnzt4M5BDW(K}Cq%Q?5w$Jj%ZrV*&E!cE;6{-U@v?SrvZud%Kv5SvbK47L zU%-3v*_}!JL1Z9KV}Je{El4P+Icxi*qZW;)rYTN?x7+m-fkzXcmTMRB(O3RA78X{v zhuu?yRIZ6y%;~a73$mDuW2F4mL*x-ldQEL>Nhi8Wt6FAc~D+(hBntHmLY4b7AAJ8ZHP9GeXO;{Pfcj3JR#bb+O=#d)J9^ zcU86NuQXVLCx=8QQxr0XaC2OEwnIZ=4Gv%W>+3*zu?k7wDZTtz%)vNeoD)zNY`$QO zY?Gie!#uCU4^I(e`AmMsXP7Jt>mT6(WxT;`ojFp}@J9lL`GJuYvdO5(Sus%Ytx01u6$Y3M-?20En&8|LQUEDVSLRiHm4 z=XBJli)lj~6s)07x54~8Vg<$I-f^Q1SvyY7Ien%Ajt7@oq! zw86ozn+{yWMYIc3`G4g83kGi4|}mDGw1rx zP&pQ0FV}{On%UdE3B2K7=nwZVWT^q#<0FTp3tXAR$JNl1RNKy;M?w_f(;&Jvcq^KP zEfTS54-}07^&~xDNdAMYb$7G&8q>&p*SFbwR#?DBL3fwML#n_7E6jl+(Woi#kABVCQAE{nu_|z4 zAj#?;1Ka3Zk49WRqa3V2E*fAgr8no9 znp}|yFIw;!&1$nz6srN#-3E4W>rM(*9)X=10#adY9GmQ*$Lm9%;~zhZsxg#iYB$|N zf-eM*?XW-kcV%2|G}5d*2pJktr%}sdAViFlu8&~tH92gd!LguX8)(#-!;OGcP%&o_ zz0{l6vn5$keqddun?ocI!uRDGI0rM|_7fw=4xp(_9m?; zT}>OOQ&+%EiIja@oVCDK%VAg6S6AR>(&|r=En z4VEPwonf95VQXN_K>7*T8IEt>=#NJyz{-!6KW}U*OIi(=Q;c|NR^4$!kuRd7Pfzpo z0Rq=pal_>7Z#GDU1kpC`>6qAe+|4J}fu>L5RbHrSGM87)CS7-)qG-%<&J>?&YYMiW zlnlPu5g@H>;6RrO7nJd|>NfmX91=?i0pZtMd6W&vVTOKk=sO!3JR8G7P@fzMONkxY zll!|g2!h0sLe|k>SuKdR%~q9ODQTl!qOCY0bbNk>EU5^%nJy z$I&m5p~!hcgK3k3OzDX9Akhv5Vf`z>cz&#%d|CXD+Ll{m;{^G8wqjpq zD21k4;eYTo%5_Tfq<>|-R1FLiXw0|Vn-Wj*HuBrsqmA+O6&3U+Lu%UQ2*-=7xnWq; z;o{erZ4a;Y9!yy+!T(y3H0iKNRumUDV!;!b(7PzC>Ob?R!x;0z{sHm^o+bF-*dGTD zDQUuk=lA;M3y<6EbrSLa@IJQ4@}-GHZ+|>UohwUQ$qFBcN*1!`S6$&kZ?{qL`!~PA zphGGRCpaMcH__Z?Cp$;~#!K?_Ie&VSlC8W6-_@A$waGq2g+K-|ot^a9`LiqE#ABd> z#sD+DH}q?IWK>nc5w<#vx}8MHTG@v$ses^$sb{AHIL%bwi)R+@$nu z0AoP45X4VV?~ciQ@%nN(qlV2~&2>04tW6ZY5L%;-yx6=t2rufWGF+py_BpG{K?ABB z_|5NX&}`v=kX=*U;bq+(Y@ZwY!a#w@XOQU2DMLuFG4B!)60l?o9-ZOQ2uMQd04+^G9e8S4O?(QBl=s219n4G7_v75K-1~g(GRL=9FVDpn*q`AoLSmxTA`}o>c0R5I0JHVLR94DHpKK zcHznCa~jCot@1ITsj!>sR?iL{C`^?WAErb-%cT+y8>4@X;DjAN(vg)lLXPGrIyZjN z1;*pHti{`N>p(Q~&Zs@Ps0wMxo9KNuAA#-GXj=1m&S6XD3MEw^(utdtq(`hG`` z6+hVi={=U)!f^LvU*0Z+VQWA99F#zzf(m2hu2F6dIT2}=-#jsj40jDlwcgmZf71a@IH)Spd11Zllb6f3_7Qx1)W=1c zZ~t9}0;^!fqH%JZ;wBBfQc!Tvx1@+8#j{)n<^lJ>pn@<^EtJWS`AyXHr;d7K4 zwTJId12gjDA}o#0avK?m7LDosxzaSN&{22l*;$S6qU-iW(gtc|9m>*J4M*T>DmzFD zIVvHg-e?W%*xgt?WHlp%f+g8%^;*=tg1@}xIu{8d45`bOIv zuqp|E13(G@al3f6qok(cq`oV8)*ALq{7( zYi*G@6>TV--nuKY({-oqTsd{npMG+jKkX`U8{uywX~qOI60luf__+N+n%)~E$DHM0 z^=%JITlu%7$*PeN2Mymq)V4Y0Z_V929!WQDZdMGTOt0F6B?KC(vT~ z&KOpYRYHJyV2?E|pRRm3*$<1QB@46CwsfHZsuBv=DC>Louh4=+&hVk>#7^8?18>|a>j!dW*?u7i5dt{+^Nl~EC0K3ttCdh4a$A1N7@{Ol+3)O5pn z`!jc9KL_mmWXsQZefx8aysF)|?3)V15yQ3Dex4UFI!Ooz@k2CQm%phkK0ve%+~JJv zmN!G%+^>~^)cziVR_;Werh|#C3>kJa>hpFcG7xofb-Nuf2tB0D|LHy2qA87}a)$c}vB z*RXl!*LC>GCVkIdN;Np2ztv;OI1kzYyu>NU6Dyp^z+m#CHVCm>URb}j}5<8q&E zl$Kj;e!Ic^GUxdj*z%O%sqo+xGAdxjzA7r8s~|@pwW-#DP`9#Ux;d0q6;dYKT+aA) zcq)#lPB&F?5g(b+G@=^A4ns{|uZ)~rdWU-jJmxi)pCi)n)Kb9QK<}J1x%p_Axldv8 zN;{pr?nDD1R`U`wJ4i#JOlQdJ4?VRDK2AkW_~DDzcX8-T+2OBW4*5LnvHwEsJ+MkF2%!i$g(%EE$~~+s`>)BDVH$IOmKgCNQev=+OI{U%&cOzV; zulHiXfmFj}J~`YOcn%dYj2z%;J_t4Zz)E~-3I-_sgptQ!DeOiD=R7jTNRW?w1XN1L~GlYsG^SYbE^Gz=2IU#>UoDgRBd!WTg z_mPAGR=wYa#zRK|{&g)OP#HGB%2+py`gMGQa?Y=aOKUmaX_p}n1LoU4VqD^DE?tMY zgNOy;e#dQT9P=1rKdrbCcnvvpv_JpiSqJ8ER&A(bi85B56nVrhKP|KE#{W0^E}>Kc z2;tJ7Sgru0_B;V0X%yuA`i`B` zS2iY4{KWT8Ue?219>%l@S?)k9lOuW_C|4lwi- z7FtAU+79kVL3!f8`<3X`5@@PzcS;}^0n&o(JH4mUpo8;2eN1oslBAB^Gn9Pp73a*e zQ<9u$n?_42VK}V@J*ck1pmNw@M6S2XtGA+_@OV$Lc#97Kx}@_`8ruUGbkEJe+aC?2 zL~4wkyMjt1@S2Rz6t#mHV5uh@335d8ZK7|gsj#Zniq5xkh6@Ft37JU*W@T0c)hq`& zs#b?`E9~-VF`T@c2`@0>H$5&479)HK>{yJE9-W!=%_UrnG&oUS%lbX*7PT1nbuxm5 z15WL9HTNosNV4PzR(^O#)>qV84nrgjSwb5EOgMYwDvypSg|`I;$xZ6GYseAegB-Hk ziPtW8kwz888&8OvQWZ3Mlf;i4anwI z{35Xt&K-oO_?iBZVZR_}zh_Xh)kk<1^U9-t3RN*4O> z(N$E*40JGQI52`LgrXMP1rfsxrhQlXfV13kyhTe|>3>G*i9jNoZBIxOO&hW;M4~xy z#jk;MZnhA*ta^k*gUED$P6J2`%}OYyFUrD8Nr5*mmmim}nsa|~&`At=vI1ael>JL+$sK%Fx4?w6b zq5ht1#w-Ww0^T!$P4IQ(nn_T>qL0mB=e~pZxn5&K=%9+`Io9cg$Rhqalg?*Fr^N)UIZ2Pu| zo1YX>?Bc*v)R5Pav38JKpb2N;lKrjCRio_RqTaJPc-;FT(Hv4}lL>tJ6SSlT z%Pd!w1DMRV2@$qS=Jz#Lg9Rl6#XNCg$PMCq)NXq%WG>4J)Ae^<(Cx(IO+wl$kHufv&KCe00*1@5YSiXGkzdBh^jw=yh zy8d@;csBM%%ED3{mmAgt^Z(6eO!)T=e@r9`tUG z*Q&`-91)SDRVB=y#4|V`gffj9On$*v!7&6j6jU;-lHd(i&;!_rZ~634Di2+%8#g~4 zK6(M_WFyB#}WU2bpB(J`*-w<$%+sy~$t9)#HD))wP_W-yw;e9i!Ma#_WgZSDg?W!8R|nNKbCUPOXT4NJzHB_*p;!_Hi9z-3??hSvjx!!>ojD_Nq*WHi3cU_cwT1kVvfkUUDd{Wsb(Ko0J28q_{^cxa0?gdAe98qh6p{BkzuPpv zfd&o-dV~ld^SCpm`v}#-zw|(0n=L?d`rvF7*2?0G5A@Y0as&o?FI?}3 z-iLHmiuWA#*(pbdyDxfMa6w5i{C_Qgoa!ITL3oHHYqcX0vae}gK!!LDh(QJ@)Z{Td z!$goqOt0xx#tI>xz=va(*C1QJT}Pv!ruh;w63Uo!Ahgm7^J=&_I|m|vs}Ih1R8Bah zG*6#_6E*kKKju*xurLe1$g((E2q?^4>_<4Ck%ByoTkN@CC^xjQnY;zgYua>&Ydx-c{Ak-h zBwpRxqx@sLh04|^)$EPfQBn&ewQb^?|M~#lFUlr!=5%HCsU{--?FZcVnr1V0NWL~s zCb8s6q2jz?{lu&27dyQ~N`PcSf&v2ldE*fbk(Lg*Y#{fI*sk9S6MTC|J2GXhq;<-^ zKp%f5mvv{VjpGsgx&Nx6aN@+|tL1;do{NqOrQ}Cb#zm|L>S}V=>Ih_#(gl8f^YS9v zd{=81gA$t&;$1pdl@v&&#aKp{LWdq6L-0EruSh=tNOWrzZraC6YkLjY<~soKH~aea zcnttte}cxdepCz#T)z62qA{5t)vG<59_V(1jQKRWKLlyFWxPvl-*I%wnBRh5gQh-B z2r*%ixu~MmA4|tiG6_df3;0geGvzV%&f|B>@_WGSU665EGpV4;-g0 z;ELpzjSJc;QD0=_>1j7#1S8rU|Iy^?H^yGTTzZ>K69Y1tWmYqWFnnU-H)Bj-wOE0G zX%-=lHE6~dkR<~Y;^5Zgef;8<&-Du`pqxf|J?pvwBIABXly;}9_F+pJDYG;kq2sLy9LQw+qFR4EcfN;qXA)64K9!a#c<`D6ePx)7N5TH zJhB4Hc9|UZEq~Fqmo`J*X!lPK18ny*6aW3*P{($k?mOH)KLe!3^?6=eb06wnw6P)a z8c^qF9{f-|s*vAq1Oy+vO$SQT@1X$A)GX54Q<#F^=IhF}p+aZ3Lr`DP+vQ5U*+f^z zlk)K%0B$j#9`5M5VvuDfx6&(Q^D)g*fIPc_BZ|fl5lU$a{XFKAK-RI4NLR&StWp(alt`q`S}u26;YdAG|$U&xWiO$y~t6T`dkl1h%O|A*pe9|J?oLI@E1 zg;_w3!LoZ%*je7!n7HIJ2`8RV080Vw+UY2O7Te-QXgtvUvAe^Xs{vNnKY)@+VF%fb z`eL1+@&ixM{qOYVmxn9ri!u#8(_7ylF`P8NtK(kvFC#TVwJi#XroZS)|J}4RAG?Dx z66`A8U)VU1mC=^~r%f=}K3*=H0a~OMoB87_d-gHlP1jBe`mvz}TPGT0O>}rT;rgQ& zAW0M^i(KtC1;cDNy^+n!pHb8n*R7mA;eoxrnKUKF3q01}Rql@2`hWi!Aow^UOoTzfCdOKzNnMHoEDHH{jwtbP(G*Q@!IlZaH+S@?6 zCY99De=ZdtZ%!!K0#Bc#?)%0P3x#W!WX)(86w`&|Yv#v(kVycwkWZDiK#h3RaQ$)U zri2P&>$1TY=r3H2m|83gN4JY=oo!v&fHMGUqxyGXzs!N;)Oa=bwxl^8Zh>N7K=0Qt zg%G#lVqM%u_=9S{{L-1YT3ZErLr$Cc|bZqz1==N+H8U8jCw*glH{cG(lHY4d~v z;h8|QC%;iTe})VU^1HJDs_(BW-3%K}Y6vRchyED`+{||SkUBmK{QG{TD_DDAqCZtv zKq+(IX|uv50}x^HYiqLR9Hk{5kULEj7$wr*n?A`tzAsET6IrA2tNK5kAp=)bjeu@- z2}c6)F1JdATcYi;J94PtqNsAub#K(gI(m82Nf$hy=a;tQm!+l7s}Y9zDWF^_aAL91 zs?qo6u$#BP^QBU{e@G(P5YhsAG5l1z+?=TU|9ZOWxG18oy|mIT-5^Le$da;jNVkBr zbT3`f-3u%oQc8C&AgFXncPY5ih=hE@`~KdKzvjo@xwAWW&V9}~^E~HhGEBKxeJxl# z_}MO$nPT~5ItZVMt1O9$zZ#8`%D;)U#e^B2RnqML@Vh=~`)a8qYiyvASYg+$0gyeB zC(=fl#1l;Azr(G+hmuJcgWp4c<^hSC18HAw-^l{P=Wjp?KvnI#gF36}MaylOOD|nW z7QdeG#IlAE=Fi;kdvd4@KfGEcMXvg#NKVHi1z)qcbCRGcOi^)cPm18Nt;MQaw`jrp z#Z+$tJp;sh>k&=VK0s?HmGFD2>7|Y56^WReK&=DPihv>U8LUS}m6)sHin^Yj(f+ii zX~IHPY{YyXn{IB@#-0o7+s^UjtUVC}vi!uLEOKjRRbomW#pr``gMH0JNt!tJ$b|(C zwLfm3+qYKvIx?Q3oslH#Z1b^-;-GeVk-g3f-);^vw6nCeR3#qt5cCU~w6w+Jw*C1j zf{UG-alDGiJ~dq@V+)dL1IQ#$Q}%q=NMDApPlk*YOl^q(!MK4Y#n(Qmbw+$cSE^*) z%#^`cLDIOj6ZP`jPp{18Uu!mbO?;Pfg_(Yv+%OuIJpa(*;Ecw#=FKjGK;?5LqWoP0 zFl_H8y`OoUXEe4C?EwDAX^Y*44HB`#gqm(lst^aUn)uf{nbcsS4~x*zgVE+^5{!=z zA?+n^{|yiP9HW#9y?r*mALl;UWxY{)*7PXb@|~cGHqp_t$&s*2+5t%VkJ^bwmPcFw z@lc4z40sWwq2s8iJ~F3)$MNwDt2hsF*G16dQwb0{CRM%%b&eti+qe?)nI1gXA_2)7 z=p@HJF$&1v0NRq^U&7{a3686CV7CeOx-{(#`Ah@Ie^5h-{EUDMncpJ~4w%(r^&cRt zU;(zy5!WtK^;F8t!f^~{ta7jn?u3TsyA@@$^Us$XI8P-2&alRxm9dW-4^F?&0R_`e zt@CBG^6aUoNJ?VnRd zpHvY2j1f#p2zd_VVkiyZqrh`y>#wX;g9J$B63N|tVO{^OFhh)6A^{4$icQ2r!XIgO z3#sULRARb1lM4W~cqMzH1W@Ui^O}q-@Z>541O0?66r5@vRXaB?o<5vK=GptXrL7-a2HbPYYbaJ+Yo&gKU%RML(y^bDCT= z0NVfLkoH)TOR0i4eNCBSIr`c$EXu%sO|a3JLT~C9T+@~(UIsH?YY(j%PTuqdDnwkS zE3jVjW7?H#>8HFhV;BMoW&r9u*)Mzm^-^j-i9$YzU3=m|#+w$cX(mVFEg}7H3Dkb% z>_{VYeb;bz2Pj4YFbB6@tN$^`hwE#9Auu;5yDez>M>ujwMM+D>nTBo?xdguWPUVtg z{QdAUiG1|Cw3HW(p50?Vt;q8#VHJ1{5Rlpzs{vp)qFQ&D! zToQf?CC~H3V!oOy`mKV$v)4cR`FHI{L)8_TL>CmZT{jh7KgH=GB@N90$l@FOok*8K zw$I1MnaFD{76}AvRsff)ZgK2jUk%*?NPc3^_5=dh3n)3~UUa+;Uvw&|Wyk~MNtKO2 zozw0{8+tT>9_QZ=nl-H_IV>JV#HS;ACNEd_0eK8S{UwQzab>l2R+Vf!cCiIG?JQ-m z7RhP`iDKcqHOK+K?=Hh5buiL~)vWnAkbLs?0SVN{p`Fk96Qc8b%=w+cTAE(%+YTjx zEN+%(9hUVdvyNHyGT4~wK~F8y&4`VCl+RG9t&f* zlhv49w&qCKJir1JJ3IBxGLQZSOZ{8SDvzmrFDBEinp zu;}3-xs4YRH;-N#_E~zYpjrY|(@ava?eEqqqeB;;Fj1c=KYd@jEzc+R>1s^PQ8h$e zT^)jCw8q`FrLVNXvIu%5tx(Po)jpFO3Yzo^5D`vBNE#L>1W5yY^%`2 zNZ=YVeJCfx$S4UVXv_qCthU>ObE@EI!O~*L$}tF8C?{)I;%4z5?sJlSuC`M@H}P=` zQjYha8H%vdM=-~&FJTbzB)bwA9+d_;YJd^M`^{l1+;b`I2DB@vEXy8hrR zr5GMN=;~^e5ORA-C^}9uza5XGz|dTbg-S+kdH_l`Q;>(z)=p=tAOM560a2mZ**FSh1oOGnS$bDQ(EMyeJv){;{z{TH zxP81!+smU9&zn|KsUi2I|Be#66`v1FvqS@}8fl@D*iHHN^Ng!ZOFx|3%tKE;;<>33 z>#w=9`iQjdG}Q2n1U;f%(y-7R-ZVs`rBWOdF!D-;YP`$JlpK2;C|Q71Ns zb$h7A&yV(SiDayNQGaT$BV#jS&`OIAauP58D&ZPP?H9eFiJyPc<+niV(w{W!Q}#LM z6w&MdB`%Jq3rBCV4_`Lz6tV7qx7MXNT|ItFIO6!`Hq>MYr|>xR)rE|{?w!+kMW|y8 zYsj5{^1oyC^%B=3M@>}F8v>8DBYaU1NQBJ!lhPIVL-X;&axXnGv*iP0*oBk0wlGI{v~8O zGtMx0*84wKb5)NL`W>7Fr#xwg*aLzqGJgjEgA>b9|$gHEL7eV6L*7LQw{8 zv~v0qWv-lmHwJHT2ad09#%9~#mH&AnLGRbmUL7--I6rNEghotbf(dV4LajXKn>!DK z{*Hx(f}xo-)4)KWCG*PKoJ6QpcBs70?n{~5o@rkuMvSmjR+!mDQ66VwW0|s~7C5w) zC4vM(vE~BH#~)fk@=3t?I@dPki4wRJGbsW^Cj8p(h3L%n6C)ww?Oh%(3;NuPdy8So z$78v1J8MCPTGb!3ax(M65{Lr4$9TUCS4-3l`Eq_heDf4rr}ZSOPd|iPx~U4#J=hCk7v4OyyUb%!+h8OZyr~%JfU%T*k)4$8wW{}-*k4DXz~j=mT$*<9-Od_i!`kgP z&B~7o&JOK*@nJjKH8}46LuU%*v3XD6-{wH@{VBPrq%$!cGyo0Qnj!P}O<|v(T_!@U zMlAV+%15P773d;dhyqMrp1vN(b&{G!1^WAD!$Z?0jf0oY3z7YTN3A6t26?;rVry90 zid28L3!6LyQf!5HdJnaA_iYb}FAICuBck8gLi4L=SaK4tCu%uh5*(Z|-?u3hvQKez zNVHa_(ejp_#G$UQ=|qRm4ZS|M>Fy}i&#qF4sI7#uVmowgc}e%yy0Q_$wkW`K6s9~= zjkqsYO*AnQTtwlvIJ(E#8l2en5X{ob8vD4J9w_tZp=d|4s1MEXVSY>Hx5Cg5bvbAV=A8q2XC~1_zXUVF`&K*#_(3H| zT5Z{u6!|yVds=maVh|%Wj1m%m_+ktS*9nuI8!qUw)r^?#-;6qA^1^$0+9Yf~C@J^- z7lqd*B6l?9t#I+ePlb|L0*Wqp33?X;pbfiKWOa6Rb(&=^E2II3jb!WX*c;GS*sxj6 zSM>bw?mYdGW(4&>io(n{hu@>uNI7)Yc)TzzDO0o~fFBOmX}S(hX6Yr0dr;xw#IbFM z0ek->>t^mx3|MA*m`fK<5D!Z9ipEig1VMKTic9kQ6;2fZsW2Se$-%v~RQE0@H6q7@ zo}xUVIvH;DE!58UKDFyc@dqx3F@Qll;#+m+FCG9r0Msy{Hg?yX_Ywp0yDO6}*W|*H zBwDHIbRLZX4|zwIxzg6WEQKF}F4y?fs$$kntnDj{`q|HxUd#)8I^ddA84U8Q*jZb< zj1lx`-)nqZYudB!+WNR}vrnpgzIa--IK7y}K`FBO=jLUzzbOB?7T^F#*s06vmyiK_ z+UDT>2na-yzMVqk8piMy==B^g-$dlRKB=LiJQT|?o!eNetEN={*4I;Uk#%MMFG+n% zv9~=#8T;_#}1la*bU>b%x|7Xm@dYdQNcj zkEwhdz6lf!oQCRdcpA|B#ljV^2&bG|{a2Gc2n#*03kf`78X1 zvewB^<2&&673K+7gktvCz$@2~eY=NAgE57=*+jGto$fk*saeNwcT=oIN>+Y{>s<5? zP+JI6y9XH+7owF`zW4{+jfS(2m#_7>IglNAE5+1Iex4jTEy&hM` zasT63c&$fyc{JOx2ZOr|-q$Q(-QPbL^N|#O7YQx?(l~BBz!>>dagZ+#j z`&E_kP?i=cUmWnNQ!D`^Vx$xqO==m2QeGYW*SEPgR13|(6w}I8^?5;;WwA(3PSL!k z|F6NiJC+0B9u2DIU2Or&3iV!*ef*v}_da4#tZlr7xybCbKQ#shKRwKa~dhFbJ=e@SoY^B=wn zG!&?yO{f8VmgeY4^lfsO6$RimV}HpIb?2fp62ckjy4}1|YWKfkd0gYI)aZehuzx4MvWkbK35e?WDiLLvhYcOHDZL>BU#b zLKORqr~2Ral0lts8M$6eARdAbJnyddKVSd)^0nx5a<8+~;4iou;D6#b`xFcCl3;0g z?}YsoJaM+7-dFoJU_~EHJ2U;n?L{q{6L>k{E(1M7UFmaMVwfKjJv}&SsbedfpttP< zoe(6_OtQJF3axF7OTna%+1ls=147c!RxW74aqGqcL98kpEvI>=EqEXhv9D%C$TFLq zQDq!1^L1UfdwDyFl2L4gMuJqH(`yuc(50c%^B@L!ZN>arsK`7VK8 zCs3JQIIjV(1iwaMVB&81_-+3b={xM_+YhU+Ba%>_2Veda@rcMDx#)TyQ`4eE%qtsE z_50&1{kJr4{{Hs3J54HwhPX=lD~^%66lf*zCW~0gg_AkB+uogwYocXoJy<)iYDlbJ z-+xrCw|(FTH(OK^t^ioLU6tG?o5W3*u1(zFZ^!++k{vOV2Cl8c^RfuELIMs{VDlLp zs6XtxOLx!FlN(bY4kT`9YM?#-1!SS@G+!%jRZv3Od)^AHuY1cs{0(K;o_EH2v7k#4J zu`PKGm15COV^EZVZ^RVJaW9q%Q*B`RJg|HXL)wUXF&e%O(@k`u1eVXMekjeR6iuT) zB|n%+d{`;xrbsChoWOva!x91e+W|a#bfe*TQC@6g#XV6`-l0sLvkRtN1iMC)rRB7I zy-?!XIPJ~kBc5kUqm+|q6S?l*k+YGOvta}Ub9&6U&u4&HOa|)kX^pVrttfy1=$f_< z%!y1WPzS<0V6v2Vcxzc=%+X67l$MTDr2cJ=N>7@wD_NK+M?a`z2k^+Od}0Y%^v2AC z>+fmGD0d6M{fFr~1F7=OeS*X!>hb@WWB}+M3BhTyPY3FjnqNTGhVBH%#dP z+kWot^G=2CmZK+0K9@I+zlOwcJ2B>33x^Ya2?3o#WX~bXH^_GXWtKVb ziytG?I<2~7+&vLMw-Z^Pw={7P@{BiTsJOTh^6AskA;SR))A$EyE<}(j<#sS&5++>{ z%hp3CfcsY%Ov}KevxE%B`QHwB@;e~;Jba0%l?FuHiQP|uJG0fh_zr3sf&vf6~MI{ zi9ZI1z=(J^c@OhaEC48!cp{r7oJW*<8-3gS-fRPZ*hB+&uN?R-S!xrAkigzolahWu zgFdy(&7$^s1vl5!N^#+wDjug7BO4&l`l43NgCy{M9z>2uR>Qf#4L+VzR(HWWJUzpR z{hbd}a}q`LRzS=o=J)ajiTUsT*u>p0nC;(rjU-A9P2eJzLZI;qC~t{E;MjV$;SqHC zIanEY6WG_kI0RiTC50-%Cb3omyD{>T+cvY0S|jJ?)4AEW*mGkFKQ5tLCS;bWW0JeB zdUE^0p4#OnpoC^qQC%NR5=~7Q2#STHUEryf+Z}_tZ$4Q zcqiWUwTU#}ur8w-&#$HgeW*tHTmc{n#vSt3myar1(!MwPcc^O9pLx3&@-7BU?$@Gc z{sU$tASbaIic?bnx?=qu&QIK~m7@MU81gmZ>Tk56dg3&G8B`NH>h44`1UI*%{?S#B zR@AS5H@hB60}H9mP4fLSw{?}nDmE8P#TmiUEDgZozu;^1f*wnh;9gsj$8!k!ZZSZc z4Z`NH^ujjQyD5$cL^pdRFWVcsDfIs4TpzVz-IwANc`f;V-8H?N4Ii!lMR06spo8@o zti-M2s!ggP?7S19uF2+6ULX*DzOuZmuBIe_;yCuBKzMQeZ&hH3S{&SQMIE$olO+3m zebJrbDI1-G`+mKK9G|rw9%WeUVZNNsb(`bCRwNMw8O5w30HiLgEVTfJJIRR|;I)3@ z)$AX8Gn1{z$S9JOlT!4;)J;Db?($^sd2v_h3pX`jk zm*;F1ZrWVPp0q(F;q9B6%>tb*t8ITEcYi<%{P*l&b!R>@_@k}88>E!$GdbT&OxpmS5!5$w6 zD^YqH8cuJ^)k-RzT~t|Ra7}vP2!*OmddVD1hKADos6qmZ1V8>)a%Pn#Z8?RYhn-=_ zqHi^4*#(}x4L7*4q#PezJVig@x5d!a1RRzGoJL`!u%Ql@w|Ej<=0m3_XMUaeykr`C z!kNI4nd0Ln^P;>r%{um*7&n@ATn)FVmeUh*3UtNm9XB?&x7YI!XQSE8dX~r*J5=heWfPBYTOv^!7Pbylb%DN=pK{X_>B`~> zzxEFd4og+77$7C|hfZ^wk)lTx>|X{27Nu50aCHRJ-RnP6*BWd;tv^*+k@{+P?L!x9 zODE_#b27K=>J`16<8AUvLFV6HU*CY9&Ur(-*fY(*GUbKAa^w{X*7}Ohqjz}oou1k6 zG~UcDK=kPw-%6Aojke%~%a-SgdNqe%TQ)2p4k!g?eUjG5syQ)|^$n9`co`HZ0v}Gr zg64Lgn!ZUv_4n&jM$bDZTV|E#Wsh#hQ)30W44_rnI%-tK6w(bNu@@v`49A!i)H(fu zMLlB5Er}Q!X-!${`LiBxU>+n~vL@UYK$1tjT%=7ZpM=J1zh1ARbwYvrngbg%`N=Z1 z{+Sz%y%DxaUB82XtgXUv@@WZ3gA^r~GMiQ(k`&Q@&M(cw3tgd&`n7+r^ivE|vtFIa zfr@MNEguir_ppg3>vYs383RM=E@8?ngbNhR)ZCKuiVqhr%5tlI(Zc;ghBOjHz2Qzn zPCa%t%a}A6YDocS&yDzZeNi6-n{CEworo%OzG@k9X+RKfo8EXEIn_tmH`PhvdlHW& z71TTX6es?Ulu5mH~c10^cb$pXRt zqQ4%5(XM9O!=4r`irDtDzrAIDA;11a;mQ3#g#+dpg@-!3^2|gC5%Z|PFCPhAS4pDW ziqwJv6u(6^Wi3)%eEdTZ+(8CONwhSUNbtn28cQW5jr;~ zw5FgpA0twFG$d!Ln zWipF$u!uq_k+QN#-?8MTcnSO6)>ZklM4;paArH?O9OXFZ;ic|w*~s@W4o=QjMn*=~ zSo`ID4iQ|C2)%Az(Cc6_TD{zGx!IdDu&`VwJ~QV@0wWXEk@ezv=GB$Y#fAOTc2-ta z3#0GHkc#T+!to3aoqj&G{&*>U5oc%TA4nvg<7~zZ?ufkli*Mh)g)g~;oz_1P50dEN zxpfIEv+i}0!g@up7KMc1bI!J7FcklUfZ^uW*5W$N(cQwA-tVS%ghbo=T^6hxXJ)kd za7i}#;NYbv*{V21+1c4xE#mGaDB+?4;-33zQ$lDKU!qFb=jP{Moqk_}2|Mb`1hB`+ zIU`@+=Y9L8G9^Si6IRT=`PqbgCn=6tK!tcXG2zPJ5bNdOa!fadDSD3KCif<@@>eaI z=P&%S1qT@v8u<}~vYnR{34G82)&38_P9P9frqw#)vnp%Io zy!;C>oI{OXVS!HlpOlVru8q-@5MCPxU3Zi8FYl)coVXKVMboYu?)I!+GGTU<-R_gZ zFmssXMTe&!vtT1`tPLV&xtD@1n*`MkogEv_9(tvMOAJ6m#_MWq4bsv{QLW;GbwVDe z7cZhjw!CcGm7){E3VA*b5$tD~Wh`9nrfa_4``xFX`BAiMWd~) zeZRY(dd4$(UcZz!_^k_Z!&g6^YICuPOdV`}PNi(DMG6JeiP88-Q|ECn9h;W05B&7P zZQ^GHjPX|sH3e1OT97c{h+g^zPVA}wT0wnN%sy2OBz`P>Frt2fYf;!3&*EcAAnZbT z$&8Iz)}*W134!ujq=21VEQyV{%i8Vl?d^545RI|vkHmhjC@YiCPN9fGF-EP!Q3T8Nn^^vYdmE6eDd|?#B2D^C zAN@flUTAl353%TY7Js8n8rd1=fA8raT7kH>DH?b4^S%1DWVyHIwxcaX{I|hsE9SpLwKfA|T=GCU2jdnbjL zpk7%gp+%?GP?4@R7fH>?5_Xp7&j;-C}9Lmbd(8Z)R zKIqVwQACA}+oDM@HLPv9q2rws-<5W}fH-uq*ZiAly%L1b7cxElUOR#owgV`c+^ku? zYD)FNVyjkZtn%uSr^+iY2+*_qr{4@qIovLEr35?rhc?qX2Pn)&+s#Z)(;YYk5rno@ zTFmDgyoETN+<#m=x2y+VMi7eA0T)p%=ODv|yu!fHAXI33nuARNW-?5El4HXth^q@~ z#-3_Kzv5CfvxlHeSQg+xwA?yOZ))FmJHs{X46zuqM0%~4+Dq5px2=VG77O+d+xfBu zQK$#d;73d4;X+vQ>{q~I6diGHAtAskfMF2xXo?YZ{V?F45-!fm zfp;Rtdi}9X@&)n!ciYSviVUWvsnT-x!QMf5E*-UE7wB7euv6T4n%CFhzxfUrKh(eBOTU@X9a>snYDQ5xePo@R2K@OaL61i|J@J8r7={#ePg7I$tjN3>ltfl z5yzDKIUbdiEsh(a+t1R@!{N}?*f^u9BDEj_>;dJos#{~zzp=7x!uI=juC-8Md1ELe zRaI4#or=(4wP~0=iw9t5b`$XWgAxa-^MlD`dqLB*q+vFg(>R~n(9SiCNOoE37`tey zQBGlsg7J(zrh6}~(>8jZe0Jw)H^u<#_h|G`l2yQe#7_Wm3gLwv^y5q`s;IdJ8N7v064k(Hm5sjSoTbR@N@^ZgWg;nx<8<$vdF>h_cOWNuD( zQynCILe>3rF>A$(8YIiQIsL6mibH(HJF@XKpEH9+vRiUmX{OjIN-;fRsj~XzKKoyB zcMBjFa;lJ&koI2CwoFOxcTEE#$+GJBGw#P~zD|tsEl~MhgVqb{TucmXg*LvkRo^K@hT-O7*DH)$OP(BIy8@ln3S74<4GLkSaKvl12Mg$|WqT2)xo%k|c zq)S#i;;HN^w@_dd{(DY&>s~SrvpmYTUN;w$4DmGnP)uu4o>}?!YZqfVl6}|AGjK@C zX|x~;C|34kh2*SLfn%vKOkGI8zn#hFAQ25Hfb--TeswP|_0||sg1+fHvi7&!&Ru?- z{pkU*Vtjyh3FI6h#w1Jp7A3XS3XQ~a*vS5Hq_l#pkU8djq8obbu}bE>mkT6)77jQ8=ekvPxNG=R$;WF%6HrfG=b z5I&8S!Nh-@#MBKqcC*~*mzQG#28izpOGrKbM++C16p|_%F2ex#CE*g_Nu+;319cQC U6~Ll#LBO9fL__|QoJIKm0kF@_5&!@I diff --git a/EasyTransfer/EasyTransfer.cpp b/avr/libraries/EasyTransfer/EasyTransfer.cpp similarity index 100% rename from EasyTransfer/EasyTransfer.cpp rename to avr/libraries/EasyTransfer/EasyTransfer.cpp diff --git a/EasyTransfer/EasyTransfer.h b/avr/libraries/EasyTransfer/EasyTransfer.h similarity index 100% rename from EasyTransfer/EasyTransfer.h rename to avr/libraries/EasyTransfer/EasyTransfer.h diff --git a/EasyTransfer/I2C_Wiring.png b/avr/libraries/EasyTransfer/I2C_Wiring.png similarity index 100% rename from EasyTransfer/I2C_Wiring.png rename to avr/libraries/EasyTransfer/I2C_Wiring.png diff --git a/README.txt b/avr/libraries/EasyTransfer/README.txt similarity index 100% rename from README.txt rename to avr/libraries/EasyTransfer/README.txt diff --git a/EasyTransfer/UARTS_Wiring.png b/avr/libraries/EasyTransfer/UARTS_Wiring.png similarity index 100% rename from EasyTransfer/UARTS_Wiring.png rename to avr/libraries/EasyTransfer/UARTS_Wiring.png diff --git a/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde similarity index 100% rename from EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde rename to avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde diff --git a/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde similarity index 100% rename from EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde rename to avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde diff --git a/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde similarity index 100% rename from EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde rename to avr/libraries/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde diff --git a/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde similarity index 100% rename from EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde rename to avr/libraries/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde diff --git a/EasyTransfer/keywords.txt b/avr/libraries/EasyTransfer/keywords.txt similarity index 100% rename from EasyTransfer/keywords.txt rename to avr/libraries/EasyTransfer/keywords.txt From e91f767b30e7574051ed29d34165867b68b86ccf Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Tue, 7 Feb 2017 15:30:45 +0100 Subject: [PATCH 40/67] Modified ArduinoRobotEasyTransfer to conform with Arduino Library Spec 2.0 --- .../README.txt | 60 ++++--- .../extras}/I2C_Wiring.png | Bin .../extras}/UARTS_Wiring.png | Bin .../keywords.txt | 6 +- .../library.properties | 9 + .../src/ArduinoRobotEasyTransfer.cpp | 157 ++++++++++++++++++ .../src/ArduinoRobotEasyTransfer.h | 93 +++++++++++ avr/libraries/EasyTransfer/EasyTransfer.cpp | 87 ---------- avr/libraries/EasyTransfer/EasyTransfer.h | 63 ------- .../EasyTransfer_2Way_wPot_Example.pde | 84 ---------- .../EasyTransfer_2Way_wServo_Example.pde | 89 ---------- .../EasyTransfer_RX_Example.pde | 40 ----- .../EasyTransfer_TX_Example.pde | 43 ----- 13 files changed, 299 insertions(+), 432 deletions(-) rename avr/libraries/{EasyTransfer => ArduinoRobotEasyTransfer}/README.txt (51%) rename avr/libraries/{EasyTransfer => ArduinoRobotEasyTransfer/extras}/I2C_Wiring.png (100%) rename avr/libraries/{EasyTransfer => ArduinoRobotEasyTransfer/extras}/UARTS_Wiring.png (100%) rename avr/libraries/{EasyTransfer => ArduinoRobotEasyTransfer}/keywords.txt (84%) create mode 100644 avr/libraries/ArduinoRobotEasyTransfer/library.properties create mode 100644 avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp create mode 100644 avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.h delete mode 100644 avr/libraries/EasyTransfer/EasyTransfer.cpp delete mode 100644 avr/libraries/EasyTransfer/EasyTransfer.h delete mode 100644 avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde delete mode 100644 avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde delete mode 100644 avr/libraries/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde delete mode 100644 avr/libraries/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde diff --git a/avr/libraries/EasyTransfer/README.txt b/avr/libraries/ArduinoRobotEasyTransfer/README.txt similarity index 51% rename from avr/libraries/EasyTransfer/README.txt rename to avr/libraries/ArduinoRobotEasyTransfer/README.txt index 98bb33b..c0b4fb6 100644 --- a/avr/libraries/EasyTransfer/README.txt +++ b/avr/libraries/ArduinoRobotEasyTransfer/README.txt @@ -1,4 +1,7 @@ -/****************************************************************** +/******************************************************************************* +* ArduinoRobotEasyTransfer library modified by Julian Sanin, +* backported from: +* * EasyTransfer Arduino Library v2.1 * details and example sketch: * http://www.billporter.info/easytransfer-arduino-library/ @@ -15,23 +18,30 @@ * 1.0 Created * 1.1 Fixed dumb Copy-paste error in header file * Added a keyword file -* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a better way -* added passing in of Serial port of different types -* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum failed, -* I'm good at dumb mistakes -* 1.7 Fixed a bug where the receive function could block for too long and never process data correctly -* Organized the examples to be Arduino IDE compatible +* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a +* better way added passing in of Serial port of different types +* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum +* failed, I'm good at dumb mistakes +* 1.7 Fixed a bug where the receive function could block for too long and +* never process data correctly +* Organized the examples to be Arduino IDE compatible * 1.8 -* Now Arduino 1.0 compatible! +* Now Arduino 1.0 compatible! * 1.81 -* Made it more cross compatible. Man, They really made us work for this one. +* Made it more cross compatible. Man, They really made us work for this +* one. * 2.0 -* Combined SoftEasyTransfer with the other two to make everything one repo -* Added EasyTransferVirtualWire library for use with Virtual Wire and cheap radios. +* Combined SoftEasyTransfer with the other two to make everything one +* repo +* Added EasyTransferVirtualWire library for use with Virtual Wire and +* cheap radios. * 2.0.1 -* VirtualWire version tested by garth@netram, bugs fixed. +* VirtualWire version tested by garth@netram, bugs fixed. * 2.1 -* Changes RX parsing buffer to dynamic allocation to conserve RAM. +* Changes RX parsing buffer to dynamic allocation to conserve RAM. +* 3.0.0 +* Imported EasyTransfer library as ArduinoRobotEasyTransfer. +* Backported ArduinoRobot modifications. * * * Limits of the Library @@ -43,17 +53,21 @@ * Header(0x06,0x85),SizeofPayload,Payload,Checksum * * -*This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - +* This program is free software: you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation, either version 3 of the License, or(at your option) any later +* version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* * -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ +* This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 +* Unported License. To view a copy of this license, visit +* http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative +* Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +*******************************************************************************/ ********************To Install************************************* diff --git a/avr/libraries/EasyTransfer/I2C_Wiring.png b/avr/libraries/ArduinoRobotEasyTransfer/extras/I2C_Wiring.png similarity index 100% rename from avr/libraries/EasyTransfer/I2C_Wiring.png rename to avr/libraries/ArduinoRobotEasyTransfer/extras/I2C_Wiring.png diff --git a/avr/libraries/EasyTransfer/UARTS_Wiring.png b/avr/libraries/ArduinoRobotEasyTransfer/extras/UARTS_Wiring.png similarity index 100% rename from avr/libraries/EasyTransfer/UARTS_Wiring.png rename to avr/libraries/ArduinoRobotEasyTransfer/extras/UARTS_Wiring.png diff --git a/avr/libraries/EasyTransfer/keywords.txt b/avr/libraries/ArduinoRobotEasyTransfer/keywords.txt similarity index 84% rename from avr/libraries/EasyTransfer/keywords.txt rename to avr/libraries/ArduinoRobotEasyTransfer/keywords.txt index d394efa..f86a96e 100644 --- a/avr/libraries/EasyTransfer/keywords.txt +++ b/avr/libraries/ArduinoRobotEasyTransfer/keywords.txt @@ -1,12 +1,13 @@ ####################################### -# Syntax Coloring Map EasyTransfer +# Syntax Coloring Map +# ArduinoRobotEasyTransfer ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### -EasyTransfer KEYWORD1 +ArduinoRobotEasyTransfer KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -19,4 +20,3 @@ begin KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### -details LITERAL1 diff --git a/avr/libraries/ArduinoRobotEasyTransfer/library.properties b/avr/libraries/ArduinoRobotEasyTransfer/library.properties new file mode 100644 index 0000000..810f00a --- /dev/null +++ b/avr/libraries/ArduinoRobotEasyTransfer/library.properties @@ -0,0 +1,9 @@ +name=ArduinoRobotEasyTransfer +version=3.0.0 +author=Bill Porter , Julian Sanin +maintainer=Julian Sanin +sentence=A library to interface the Arduino Robot boards. +paragraph=Supports Arduino Robot Control & Motor Board. +category=Uncategorized +url=https://github.com/j54n1n/arduinorobot +architectures=avr diff --git a/avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp b/avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp new file mode 100644 index 0000000..3c974d2 --- /dev/null +++ b/avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp @@ -0,0 +1,157 @@ +/******************************************************************************* +* ArduinoRobotEasyTransfer library modified by Julian Sanin, +* backported from: +* +* EasyTransfer Arduino Library v2.1 +* details and example sketch: +* http://www.billporter.info/easytransfer-arduino-library/ +* +* Brought to you by: +* Bill Porter +* www.billporter.info +* +* Major props to Mathieu Alorent (kumy) for +* I2C version and the pretty pictures. +* +* +* Lib version history +* 1.0 Created +* 1.1 Fixed dumb Copy-paste error in header file +* Added a keyword file +* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a +* better way added passing in of Serial port of different types +* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum +* failed, I'm good at dumb mistakes +* 1.7 Fixed a bug where the receive function could block for too long and +* never process data correctly +* Organized the examples to be Arduino IDE compatible +* 1.8 +* Now Arduino 1.0 compatible! +* 1.81 +* Made it more cross compatible. Man, They really made us work for this +* one. +* 2.0 +* Combined SoftEasyTransfer with the other two to make everything one +* repo +* Added EasyTransferVirtualWire library for use with Virtual Wire and +* cheap radios. +* 2.0.1 +* VirtualWire version tested by garth@netram, bugs fixed. +* 2.1 +* Changes RX parsing buffer to dynamic allocation to conserve RAM. +* 3.0 +* Imported EasyTransfer library as ArduinoRobotEasyTransfer. +* Backported ArduinoRobot modifications. +* +* +* Limits of the Library +* You can change the Serial port, +* but the Struct size must not pass 255 bytes +* VirtualWire Version Struct can'e be bigger then 26 bytes +* +* The protcol is as follows: +* Header(0x06,0x85),SizeofPayload,Payload,Checksum +* +* +* This program is free software: you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation, either version 3 of the License, or(at your option) any later +* version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* +* This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 +* Unported License. To view a copy of this license, visit +* http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative +* Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +*******************************************************************************/ +#include "ArduinoRobotEasyTransfer.h" + + + + +//Captures address and size of struct +void ArduinoRobotEasyTransfer::begin(uint8_t * ptr, uint8_t length, Stream *theStream){ + address = ptr; + size = length; + _stream = theStream; + + //dynamic creation of rx parsing buffer in RAM + rx_buffer = (uint8_t*) malloc(size+1); +} + +//Sends out struct in binary, with header, length info and checksum +void ArduinoRobotEasyTransfer::sendData(){ + uint8_t CS = size; + _stream->write(0x06); + _stream->write(0x85); + _stream->write(size); + for(int i = 0; iwrite(*(address+i)); + } + _stream->write(CS); + +} + +boolean ArduinoRobotEasyTransfer::receiveData(){ + + //start off by looking for the header bytes. If they were already found in a previous call, skip it. + if(rx_len == 0){ + //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. + if(_stream->available() >= 3){ + //this will block until a 0x06 is found or buffer size becomes less then 3. + while(_stream->read() != 0x06) { + //This will trash any preamble junk in the serial buffer + //but we need to make sure there is enough in the buffer to process while we trash the rest + //if the buffer becomes too empty, we will escape and try again on the next call + if(_stream->available() < 3) + return false; + } + if (_stream->read() == 0x85){ + rx_len = _stream->read(); + //make sure the binary structs on both Arduinos are the same size. + if(rx_len != size){ + rx_len = 0; + return false; + } + } + } + } + + //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. + if(rx_len != 0){ + while(_stream->available() && rx_array_inx <= rx_len){ + rx_buffer[rx_array_inx++] = _stream->read(); + } + + if(rx_len == (rx_array_inx-1)){ + //seem to have got whole message + //last uint8_t is CS + calc_CS = rx_len; + for (int i = 0; i +* +* This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 +* Unported License. To view a copy of this license, visit +* http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative +* Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +*******************************************************************************/ +#ifndef ARDUINO_ROBOT_EASY_TRANSFER_H +#define ARDUINO_ROBOT_EASY_TRANSFER_H + +#include +#include + +class ArduinoRobotEasyTransfer { +public: + void begin(uint8_t *, uint8_t, Stream *theStream); + void sendData(); + boolean receiveData(); +private: + Stream *_stream; + //NewSoftSerial *_serial; + uint8_t * address; //address of struct + uint8_t size; //size of struct + uint8_t * rx_buffer; //address for temporary storage and parsing buffer + uint8_t rx_array_inx; //index for RX parsing buffer + uint8_t rx_len; //RX packet length according to the packet + uint8_t calc_CS; //calculated Chacksum +}; + +#endif diff --git a/avr/libraries/EasyTransfer/EasyTransfer.cpp b/avr/libraries/EasyTransfer/EasyTransfer.cpp deleted file mode 100644 index aec45da..0000000 --- a/avr/libraries/EasyTransfer/EasyTransfer.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "EasyTransfer.h" - - - - -//Captures address and size of struct -void EasyTransfer::begin(uint8_t * ptr, uint8_t length, Stream *theStream){ - address = ptr; - size = length; - _stream = theStream; - - //dynamic creation of rx parsing buffer in RAM - rx_buffer = (uint8_t*) malloc(size+1); -} - -//Sends out struct in binary, with header, length info and checksum -void EasyTransfer::sendData(){ - uint8_t CS = size; - _stream->write(0x06); - _stream->write(0x85); - _stream->write(size); - for(int i = 0; iwrite(*(address+i)); - } - _stream->write(CS); - -} - -boolean EasyTransfer::receiveData(){ - - //start off by looking for the header bytes. If they were already found in a previous call, skip it. - if(rx_len == 0){ - //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. - if(_stream->available() >= 3){ - //this will block until a 0x06 is found or buffer size becomes less then 3. - while(_stream->read() != 0x06) { - //This will trash any preamble junk in the serial buffer - //but we need to make sure there is enough in the buffer to process while we trash the rest - //if the buffer becomes too empty, we will escape and try again on the next call - if(_stream->available() < 3) - return false; - } - if (_stream->read() == 0x85){ - rx_len = _stream->read(); - //make sure the binary structs on both Arduinos are the same size. - if(rx_len != size){ - rx_len = 0; - return false; - } - } - } - } - - //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. - if(rx_len != 0){ - while(_stream->available() && rx_array_inx <= rx_len){ - rx_buffer[rx_array_inx++] = _stream->read(); - } - - if(rx_len == (rx_array_inx-1)){ - //seem to have got whole message - //last uint8_t is CS - calc_CS = rx_len; - for (int i = 0; i -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ -#ifndef EasyTransfer_h -#define EasyTransfer_h - - -//make it a little prettier on the front end. -#define details(name) (byte*)&name,sizeof(name) - -//Not neccessary, but just in case. -#if ARDUINO > 22 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif -#include "Stream.h" -//#include -//#include -//#include -//#include -//#include - -class EasyTransfer { -public: -void begin(uint8_t *, uint8_t, Stream *theStream); -//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); -void sendData(); -boolean receiveData(); -private: -Stream *_stream; -//NewSoftSerial *_serial; -uint8_t * address; //address of struct -uint8_t size; //size of struct -uint8_t * rx_buffer; //address for temporary storage and parsing buffer -uint8_t rx_array_inx; //index for RX parsing buffer -uint8_t rx_len; //RX packet length according to the packet -uint8_t calc_CS; //calculated Chacksum -}; - - - -#endif diff --git a/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde deleted file mode 100644 index 8b36ddc..0000000 --- a/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wPot_Example/EasyTransfer_2Way_wPot_Example.pde +++ /dev/null @@ -1,84 +0,0 @@ -/*This is an example of the EasyTransfer Library 2way communications. - -The sketch is for the Arduino with a potentiometer attached to analog pin 0. - -This other Arduino has the servo attached to pin 9. -Both have a putton attached to pin 12 and output a status using the LED on pin 13. - -The idea is each arduino will read the status of the button attached to it, and send it -to the other Arduino, which will toggle it's LED based on the others button. The button -should connect pin 12 to ground when pushed. - -And the Arduino with the potentiometer will send it's value to the one with the servo. -The servo will move to the position based on the potentiometer. -*/ - - - -#include - -//create two objects -EasyTransfer ETin, ETout; - - -struct RECEIVE_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to receive - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t buttonstate; -}; - -struct SEND_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to receive - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t buttonstate; - int16_t servoval; -}; - -//give a name to the group of data -RECEIVE_DATA_STRUCTURE rxdata; -SEND_DATA_STRUCTURE txdata; - - -void setup(){ - Serial.begin(9600); - //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. - ETin.begin(details(rxdata), &Serial); - ETout.begin(details(txdata), &Serial); - - pinMode(13, OUTPUT); - //enable pull-up - pinMode(12, INPUT_PULLUP); - -} - -void loop(){ - - //first, lets read our potentiometer and button and store it in our data structure - txdata.servoval = analogRead(0); - - if(!digitalRead(12)) - txdata.buttonstate = HIGH; - else - txdata.buttonstate = LOW; - - //then we will go ahead and send that data out - ETout.sendData(); - - //there's a loop here so that we run the recieve function more often then the - //transmit function. This is important due to the slight differences in - //the clock speed of different Arduinos. If we didn't do this, messages - //would build up in the buffer and appear to cause a delay. - for(int i=0; i<5; i++){ - //remember, you could use an if() here to check for new data, this time it's not needed. - ETin.receiveData(); - - //set our LED on or off based on what we received from the other Arduino - digitalWrite(13, rxdata.buttonstate); - - //delay - delay(10); - } - - //delay for good measure - delay(10); -} diff --git a/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde deleted file mode 100644 index 832ac6b..0000000 --- a/avr/libraries/EasyTransfer/examples/EasyTransfer_2Way_wServo_Example/EasyTransfer_2Way_wServo_Example.pde +++ /dev/null @@ -1,89 +0,0 @@ -/*This is an example of the EasyTransfer Library 2way communications. - -This sketch is for the Arduino with the servo attached to pin 9. - -The other Arduino has a potentiometer attached to analog pin 0. -Both have a putton attached to pin 12 and output a status using the LED on pin 13. - -The idea is each arduino will read the status of the button attached to it, and send it -to the other Arduino, which will toggle it's LED based on the others button. The button -should connect pin 12 to ground when pushed. - -And the Arduino with the potentiometer will send it's value to the one with the servo. -The servo will move to the position based on the potentiometer. -*/ - -#include -#include - -//create two objects -EasyTransfer ETin, ETout; -//create servo -Servo myservo; - -struct RECEIVE_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to receive - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t buttonstate; - int16_t servoval; -}; - -struct SEND_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to receive - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t buttonstate; -}; - - -//give a name to the group of data -RECEIVE_DATA_STRUCTURE rxdata; -SEND_DATA_STRUCTURE txdata; - - -void setup(){ - Serial.begin(9600); - //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. - ETin.begin(details(rxdata), &Serial); - ETout.begin(details(txdata), &Serial); - - pinMode(13, OUTPUT); - //enable pull-up - pinMode(12, INPUT_PULLUP); - - myservo.attach(9); -} - -void loop(){ - - //first, lets read our button and store it in our data structure - if(!digitalRead(12)) - txdata.buttonstate = HIGH; - else - txdata.buttonstate = LOW; - - //then we will go ahead and send that data out - ETout.sendData(); - - //there's a loop here so that we run the recieve function more often then the - //transmit function. This is important due to the slight differences in - //the clock speed of different Arduinos. If we didn't do this, messages - //would build up in the buffer and appear to cause a delay. - - for(int i=0; i<5; i++){ - //remember, you could use an if() here to check for new data, this time it's not needed. - ETin.receiveData(); - - //set our LED on or off based on what we received from the other Arduino - digitalWrite(13, rxdata.buttonstate); - - //set our servo position based on what we received from the other Arduino - //we will also map the ADC value to a servo value - myservo.write(map(rxdata.servoval, 0, 1023, 0, 179)); - - //delay - delay(10); - } - - //delay for good measure - delay(10); -} diff --git a/avr/libraries/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde deleted file mode 100644 index 0a25b87..0000000 --- a/avr/libraries/EasyTransfer/examples/EasyTransfer_RX_Example/EasyTransfer_RX_Example.pde +++ /dev/null @@ -1,40 +0,0 @@ -#include - -//create object -EasyTransfer ET; - -struct RECEIVE_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to receive - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -RECEIVE_DATA_STRUCTURE mydata; - -void setup(){ - Serial.begin(9600); - //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. - ET.begin(details(mydata), &Serial); - - pinMode(13, OUTPUT); - -} - -void loop(){ - //check and see if a data packet has come in. - if(ET.receiveData()){ - //this is how you access the variables. [name of the group].[variable name] - //since we have data, we will blink it out. - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - } - - //you should make this delay shorter then your transmit delay or else messages could be lost - delay(250); -} diff --git a/avr/libraries/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde b/avr/libraries/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde deleted file mode 100644 index 8221cb2..0000000 --- a/avr/libraries/EasyTransfer/examples/EasyTransfer_TX_Example/EasyTransfer_TX_Example.pde +++ /dev/null @@ -1,43 +0,0 @@ -#include - -//create object -EasyTransfer ET; - -struct SEND_DATA_STRUCTURE{ - //put your variable definitions here for the data you want to send - //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO - int16_t blinks; - int16_t pause; -}; - -//give a name to the group of data -SEND_DATA_STRUCTURE mydata; - -void setup(){ - Serial.begin(9600); - //start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. - ET.begin(details(mydata), &Serial); - - pinMode(13, OUTPUT); - - randomSeed(analogRead(0)); - -} - -void loop(){ - //this is how you access the variables. [name of the group].[variable name] - mydata.blinks = random(5); - mydata.pause = random(5); - //send the data - ET.sendData(); - - //Just for fun, we will blink it out too - for(int i = mydata.blinks; i>0; i--){ - digitalWrite(13, HIGH); - delay(mydata.pause * 100); - digitalWrite(13, LOW); - delay(mydata.pause * 100); - } - - delay(5000); -} From 2f7917d43c24487c038c55fafcce05b1a3943641 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Thu, 9 Feb 2017 13:42:40 +0100 Subject: [PATCH 41/67] Renamed ArduinoRobotEasyTransfer library to IntegerEasyTransfer and backported Arduino Robot modifications --- .../src/ArduinoRobotEasyTransfer.cpp | 157 -------------- .../src/ArduinoRobotEasyTransfer.h | 93 -------- .../README.txt | 56 ++--- .../extras/I2C_Wiring.png | Bin .../extras/UARTS_Wiring.png | Bin .../keywords.txt | 12 +- .../library.properties | 6 +- .../src/IntegerEasyTransfer.cpp | 200 ++++++++++++++++++ .../src/IntegerEasyTransfer.h | 154 ++++++++++++++ 9 files changed, 394 insertions(+), 284 deletions(-) delete mode 100644 avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp delete mode 100644 avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.h rename avr/libraries/{ArduinoRobotEasyTransfer => IntegerEasyTransfer}/README.txt (62%) rename avr/libraries/{ArduinoRobotEasyTransfer => IntegerEasyTransfer}/extras/I2C_Wiring.png (100%) rename avr/libraries/{ArduinoRobotEasyTransfer => IntegerEasyTransfer}/extras/UARTS_Wiring.png (100%) rename avr/libraries/{ArduinoRobotEasyTransfer => IntegerEasyTransfer}/keywords.txt (79%) rename avr/libraries/{ArduinoRobotEasyTransfer => IntegerEasyTransfer}/library.properties (72%) create mode 100644 avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp create mode 100644 avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h diff --git a/avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp b/avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp deleted file mode 100644 index 3c974d2..0000000 --- a/avr/libraries/ArduinoRobotEasyTransfer/src/ArduinoRobotEasyTransfer.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************* -* ArduinoRobotEasyTransfer library modified by Julian Sanin, -* backported from: -* -* EasyTransfer Arduino Library v2.1 -* details and example sketch: -* http://www.billporter.info/easytransfer-arduino-library/ -* -* Brought to you by: -* Bill Porter -* www.billporter.info -* -* Major props to Mathieu Alorent (kumy) for -* I2C version and the pretty pictures. -* -* -* Lib version history -* 1.0 Created -* 1.1 Fixed dumb Copy-paste error in header file -* Added a keyword file -* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a -* better way added passing in of Serial port of different types -* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum -* failed, I'm good at dumb mistakes -* 1.7 Fixed a bug where the receive function could block for too long and -* never process data correctly -* Organized the examples to be Arduino IDE compatible -* 1.8 -* Now Arduino 1.0 compatible! -* 1.81 -* Made it more cross compatible. Man, They really made us work for this -* one. -* 2.0 -* Combined SoftEasyTransfer with the other two to make everything one -* repo -* Added EasyTransferVirtualWire library for use with Virtual Wire and -* cheap radios. -* 2.0.1 -* VirtualWire version tested by garth@netram, bugs fixed. -* 2.1 -* Changes RX parsing buffer to dynamic allocation to conserve RAM. -* 3.0 -* Imported EasyTransfer library as ArduinoRobotEasyTransfer. -* Backported ArduinoRobot modifications. -* -* -* Limits of the Library -* You can change the Serial port, -* but the Struct size must not pass 255 bytes -* VirtualWire Version Struct can'e be bigger then 26 bytes -* -* The protcol is as follows: -* Header(0x06,0x85),SizeofPayload,Payload,Checksum -* -* -* This program is free software: you can redistribute it and/or modify it under -* the terms of the GNU General Public License as published by the Free Software -* Foundation, either version 3 of the License, or(at your option) any later -* version. -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* -* This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 -* Unported License. To view a copy of this license, visit -* http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative -* Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -*******************************************************************************/ -#include "ArduinoRobotEasyTransfer.h" - - - - -//Captures address and size of struct -void ArduinoRobotEasyTransfer::begin(uint8_t * ptr, uint8_t length, Stream *theStream){ - address = ptr; - size = length; - _stream = theStream; - - //dynamic creation of rx parsing buffer in RAM - rx_buffer = (uint8_t*) malloc(size+1); -} - -//Sends out struct in binary, with header, length info and checksum -void ArduinoRobotEasyTransfer::sendData(){ - uint8_t CS = size; - _stream->write(0x06); - _stream->write(0x85); - _stream->write(size); - for(int i = 0; iwrite(*(address+i)); - } - _stream->write(CS); - -} - -boolean ArduinoRobotEasyTransfer::receiveData(){ - - //start off by looking for the header bytes. If they were already found in a previous call, skip it. - if(rx_len == 0){ - //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. - if(_stream->available() >= 3){ - //this will block until a 0x06 is found or buffer size becomes less then 3. - while(_stream->read() != 0x06) { - //This will trash any preamble junk in the serial buffer - //but we need to make sure there is enough in the buffer to process while we trash the rest - //if the buffer becomes too empty, we will escape and try again on the next call - if(_stream->available() < 3) - return false; - } - if (_stream->read() == 0x85){ - rx_len = _stream->read(); - //make sure the binary structs on both Arduinos are the same size. - if(rx_len != size){ - rx_len = 0; - return false; - } - } - } - } - - //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. - if(rx_len != 0){ - while(_stream->available() && rx_array_inx <= rx_len){ - rx_buffer[rx_array_inx++] = _stream->read(); - } - - if(rx_len == (rx_array_inx-1)){ - //seem to have got whole message - //last uint8_t is CS - calc_CS = rx_len; - for (int i = 0; i -* -* This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 -* Unported License. To view a copy of this license, visit -* http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative -* Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -*******************************************************************************/ -#ifndef ARDUINO_ROBOT_EASY_TRANSFER_H -#define ARDUINO_ROBOT_EASY_TRANSFER_H - -#include -#include - -class ArduinoRobotEasyTransfer { -public: - void begin(uint8_t *, uint8_t, Stream *theStream); - void sendData(); - boolean receiveData(); -private: - Stream *_stream; - //NewSoftSerial *_serial; - uint8_t * address; //address of struct - uint8_t size; //size of struct - uint8_t * rx_buffer; //address for temporary storage and parsing buffer - uint8_t rx_array_inx; //index for RX parsing buffer - uint8_t rx_len; //RX packet length according to the packet - uint8_t calc_CS; //calculated Chacksum -}; - -#endif diff --git a/avr/libraries/ArduinoRobotEasyTransfer/README.txt b/avr/libraries/IntegerEasyTransfer/README.txt similarity index 62% rename from avr/libraries/ArduinoRobotEasyTransfer/README.txt rename to avr/libraries/IntegerEasyTransfer/README.txt index c0b4fb6..6526ee0 100644 --- a/avr/libraries/ArduinoRobotEasyTransfer/README.txt +++ b/avr/libraries/IntegerEasyTransfer/README.txt @@ -1,34 +1,33 @@ /******************************************************************************* -* ArduinoRobotEasyTransfer library modified by Julian Sanin, -* backported from: +* IntegerEasyTransfer 1.0.0 library modified by Julian Sanin, sourced from: * * EasyTransfer Arduino Library v2.1 -* details and example sketch: -* http://www.billporter.info/easytransfer-arduino-library/ +* details and example sketch: +* http://www.billporter.info/easytransfer-arduino-library/ * -* Brought to you by: +* Brought to you by: * Bill Porter * www.billporter.info * -* Major props to Mathieu Alorent (kumy) for -* I2C version and the pretty pictures. +* Major props to Mathieu Alorent (kumy) for +* I2C version and the pretty pictures. * * * Lib version history -* 1.0 Created -* 1.1 Fixed dumb Copy-paste error in header file -* Added a keyword file -* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a +* 1.0 Created +* 1.1 Fixed dumb Copy-paste error in header file +* Added a keyword file +* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a * better way added passing in of Serial port of different types -* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum +* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum * failed, I'm good at dumb mistakes -* 1.7 Fixed a bug where the receive function could block for too long and +* 1.7 Fixed a bug where the receive function could block for too long and * never process data correctly -* Organized the examples to be Arduino IDE compatible -* 1.8 -* Now Arduino 1.0 compatible! +* Organized the examples to be Arduino IDE compatible +* 1.8 +* Now Arduino 1.0 compatible! * 1.81 -* Made it more cross compatible. Man, They really made us work for this +* Made it more cross compatible. Man, They really made us work for this * one. * 2.0 * Combined SoftEasyTransfer with the other two to make everything one @@ -38,21 +37,24 @@ * 2.0.1 * VirtualWire version tested by garth@netram, bugs fixed. * 2.1 -* Changes RX parsing buffer to dynamic allocation to conserve RAM. -* 3.0.0 -* Imported EasyTransfer library as ArduinoRobotEasyTransfer. -* Backported ArduinoRobot modifications. +* Changes RX parsing buffer to dynamic allocation to conserve RAM. +* 1.0.0 +* Imported EasyTransfer library as IntegerEasyTransfer. +* Backported ArduinoRobot modifications and restarted version numbering. +* The library supports a maximum of 20 uint8_t or 10 int16_t values. +* Mixed uint8_t and int16_t are allowed but care should be taken that the +* values do not overflow the data buffer. * * * Limits of the Library -* You can change the Serial port, -* but the Struct size must not pass 255 bytes -* VirtualWire Version Struct can'e be bigger then 26 bytes +* You can change the Serial port, +* but the Struct size must not pass 255 bytes +* VirtualWire Version Struct can'e be bigger then 26 bytes * * The protcol is as follows: -* Header(0x06,0x85),SizeofPayload,Payload,Checksum -* -* +* Header(0x06,0x85),SizeofPayload,Payload,Checksum +* +* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or(at your option) any later diff --git a/avr/libraries/ArduinoRobotEasyTransfer/extras/I2C_Wiring.png b/avr/libraries/IntegerEasyTransfer/extras/I2C_Wiring.png similarity index 100% rename from avr/libraries/ArduinoRobotEasyTransfer/extras/I2C_Wiring.png rename to avr/libraries/IntegerEasyTransfer/extras/I2C_Wiring.png diff --git a/avr/libraries/ArduinoRobotEasyTransfer/extras/UARTS_Wiring.png b/avr/libraries/IntegerEasyTransfer/extras/UARTS_Wiring.png similarity index 100% rename from avr/libraries/ArduinoRobotEasyTransfer/extras/UARTS_Wiring.png rename to avr/libraries/IntegerEasyTransfer/extras/UARTS_Wiring.png diff --git a/avr/libraries/ArduinoRobotEasyTransfer/keywords.txt b/avr/libraries/IntegerEasyTransfer/keywords.txt similarity index 79% rename from avr/libraries/ArduinoRobotEasyTransfer/keywords.txt rename to avr/libraries/IntegerEasyTransfer/keywords.txt index f86a96e..56352c6 100644 --- a/avr/libraries/ArduinoRobotEasyTransfer/keywords.txt +++ b/avr/libraries/IntegerEasyTransfer/keywords.txt @@ -1,21 +1,25 @@ ####################################### # Syntax Coloring Map -# ArduinoRobotEasyTransfer +# IntegerEasyTransfer ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### -ArduinoRobotEasyTransfer KEYWORD1 +IntegerEasyTransfer KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### + +begin KEYWORD2 sendData KEYWORD2 receiveData KEYWORD2 -begin KEYWORD2 - +writeByte KEYWORD2 +writeInt KEYWORD2 +readByte KEYWORD2 +readInt KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/avr/libraries/ArduinoRobotEasyTransfer/library.properties b/avr/libraries/IntegerEasyTransfer/library.properties similarity index 72% rename from avr/libraries/ArduinoRobotEasyTransfer/library.properties rename to avr/libraries/IntegerEasyTransfer/library.properties index 810f00a..5d6335c 100644 --- a/avr/libraries/ArduinoRobotEasyTransfer/library.properties +++ b/avr/libraries/IntegerEasyTransfer/library.properties @@ -1,8 +1,8 @@ -name=ArduinoRobotEasyTransfer -version=3.0.0 +name=IntegerEasyTransfer +version=1.0.0 author=Bill Porter , Julian Sanin maintainer=Julian Sanin -sentence=A library to interface the Arduino Robot boards. +sentence=A library to interface Arduino boards. paragraph=Supports Arduino Robot Control & Motor Board. category=Uncategorized url=https://github.com/j54n1n/arduinorobot diff --git a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp new file mode 100644 index 0000000..d28c73b --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp @@ -0,0 +1,200 @@ +/******************************************************************************* +* IntegerEasyTransfer 1.0.0 library modified by Julian Sanin, sourced from: +* +* EasyTransfer Arduino Library v2.1 +* details and example sketch: +* http://www.billporter.info/easytransfer-arduino-library/ +* +* Brought to you by: +* Bill Porter +* www.billporter.info +* +* Major props to Mathieu Alorent (kumy) for +* I2C version and the pretty pictures. +* +* +* Lib version history +* 1.0 Created +* 1.1 Fixed dumb Copy-paste error in header file +* Added a keyword file +* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a +* better way added passing in of Serial port of different types +* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum +* failed, I'm good at dumb mistakes +* 1.7 Fixed a bug where the receive function could block for too long and +* never process data correctly +* Organized the examples to be Arduino IDE compatible +* 1.8 +* Now Arduino 1.0 compatible! +* 1.81 +* Made it more cross compatible. Man, They really made us work for this +* one. +* 2.0 +* Combined SoftEasyTransfer with the other two to make everything one +* repo +* Added EasyTransferVirtualWire library for use with Virtual Wire and +* cheap radios. +* 2.0.1 +* VirtualWire version tested by garth@netram, bugs fixed. +* 2.1 +* Changes RX parsing buffer to dynamic allocation to conserve RAM. +* 1.0.0 +* Imported EasyTransfer library as IntegerEasyTransfer. +* Backported ArduinoRobot modifications and restarted version numbering. +* The library supports a maximum of 20 uint8_t or 10 int16_t values. +* Mixed uint8_t and int16_t are allowed but care should be taken that the +* values do not overflow the data buffer. +* +* +* Limits of the Library +* You can change the Serial port, +* but the Struct size must not pass 255 bytes +* VirtualWire Version Struct can'e be bigger then 26 bytes +* +* The protcol is as follows: +* Header(0x06,0x85),SizeofPayload,Payload,Checksum +* +* +* This program is free software: you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation, either version 3 of the License, or(at your option) any later +* version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* +* This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 +* Unported License. To view a copy of this license, visit +* http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative +* Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +*******************************************************************************/ +#include "IntegerEasyTransfer.h" + +void IntegerEasyTransfer::begin(Stream *theStream) { + _stream = theStream; + + _resetData(); +} + +//Sends out data in binary, with header, length info and checksum +void IntegerEasyTransfer::sendData() { + uint8_t CS = _size; + _stream->write(0x06); + _stream->write(0x85); + _stream->write(_size); + for (int i = 0; i<_size; i++) { + CS ^= *(_data + i); + _stream->write(*(_data + i)); + } + _stream->write(CS); + + _resetData(); +} + +bool IntegerEasyTransfer::receiveData() { + + //start off by looking for the header bytes. If they were already found in a + //previous call, skip it. + if (_rx_len == 0) { + //this size check may be redundant due to the size check below, but for + //now I'll leave it the way it is. + if (_stream->available() >= 3) { + //this will block until a 0x06 is found or buffer size becomes less + //then 3. + while (_stream->read() != 0x06) { + //This will trash any preamble junk in the serial buffer + //but we need to make sure there is enough in the buffer to + //process while we trash the rest if the buffer becomes too + //empty, we will escape and try again on the next call + if (_stream->available() < 3) + return false; + } + if (_stream->read() == 0x85) { + _rx_len = _stream->read(); + + _resetData(); + } + } + } + + //we get here if we already found the header bytes, the struct size matched + //what we know, and now we are byte aligned. + if (_rx_len != 0) { + while (_stream->available() && _rx_array_inx <= _rx_len) { + _data[_rx_array_inx++] = _stream->read(); + } + + if (_rx_len == (_rx_array_inx - 1)) { + //seem to have got whole message + //last uint8_t is CS + _calc_CS = _rx_len; + for (int i = 0; i<_rx_len; i++) { + _calc_CS ^= _data[i]; + } + + if (_calc_CS == _data[_rx_array_inx - 1]) {//CS good + + _size = _rx_len; + + _rx_len = 0; + _rx_array_inx = 0; + return true; + } + + else { + + _resetData(); + + //failed checksum, need to clear this out anyway + _rx_len = 0; + _rx_array_inx = 0; + return false; + } + + } + } + + return false; +} + +void IntegerEasyTransfer::writeByte(uint8_t data) { + if (_position < MAX_DATA_SIZE) { + _data[_position++] = data; + _size++; + } +} + +void IntegerEasyTransfer::writeInt(int16_t data) { + if (_position < (MAX_DATA_SIZE - 1)) { + _data[_position++] = (data >> 8); + _data[_position++] = data; + _size += 2; + } +} + +uint8_t IntegerEasyTransfer::readByte() { + if (_position >= _size) { + return 0; + } + return _data[_position++]; +} + +int16_t IntegerEasyTransfer::readInt() { + if ((_position + 1) >= _size) { + return 0; + } + int16_t data_1 = (_data[_position++] << 8); + int16_t data_2 = _data[_position++]; + int16_t data = data_1 | data_2; + return data; +} + +void IntegerEasyTransfer::_resetData() { + for (uint8_t i = 0; i < MAX_DATA_SIZE; i++) { + _data[i] = 0; + } + _size = 0; + _position = 0; +} diff --git a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h new file mode 100644 index 0000000..5384ec3 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h @@ -0,0 +1,154 @@ +/******************************************************************************* +* IntegerEasyTransfer 1.0.0 library modified by Julian Sanin, sourced from: +* +* EasyTransfer Arduino Library v2.1 +* details and example sketch: +* http://www.billporter.info/easytransfer-arduino-library/ +* +* Brought to you by: +* Bill Porter +* www.billporter.info +* +* Major props to Mathieu Alorent (kumy) for +* I2C version and the pretty pictures. +* +* +* Lib version history +* 1.0 Created +* 1.1 Fixed dumb Copy-paste error in header file +* Added a keyword file +* 1.5 Forked lib into Software and Hardware Serial branches, I don't know a +* better way added passing in of Serial port of different types +* 1.6 Fixed bug where it wasn't clearing out the buffers if the CheckSum +* failed, I'm good at dumb mistakes +* 1.7 Fixed a bug where the receive function could block for too long and +* never process data correctly +* Organized the examples to be Arduino IDE compatible +* 1.8 +* Now Arduino 1.0 compatible! +* 1.81 +* Made it more cross compatible. Man, They really made us work for this +* one. +* 2.0 +* Combined SoftEasyTransfer with the other two to make everything one +* repo +* Added EasyTransferVirtualWire library for use with Virtual Wire and +* cheap radios. +* 2.0.1 +* VirtualWire version tested by garth@netram, bugs fixed. +* 2.1 +* Changes RX parsing buffer to dynamic allocation to conserve RAM. +* 1.0.0 +* Imported EasyTransfer library as IntegerEasyTransfer. +* Backported ArduinoRobot modifications and restarted version numbering. +* The library supports a maximum of 20 uint8_t or 10 int16_t values. +* Mixed uint8_t and int16_t are allowed but care should be taken that the +* values do not overflow the data buffer. +* +* +* Limits of the Library +* You can change the Serial port, +* but the Struct size must not pass 255 bytes +* VirtualWire Version Struct can'e be bigger then 26 bytes +* +* The protcol is as follows: +* Header(0x06,0x85),SizeofPayload,Payload,Checksum +* +* +* This program is free software: you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation, either version 3 of the License, or(at your option) any later +* version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* +* This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 +* Unported License. To view a copy of this license, visit +* http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative +* Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. +*******************************************************************************/ +#ifndef INTEGER_EASY_TRANSFER_H +#define INTEGER_EASY_TRANSFER_H + +#include +#include + +class IntegerEasyTransfer { + + enum { MAX_DATA_SIZE = 20 }; + +public: + /// + /// Start protocol with given transport stream. + /// + /// + /// The reference to the transport stream implementation. + /// + void begin(Stream *theStream); + + /// + /// Sends out data in binary, with header, length info and checksum. + /// + void sendData(); + + /// + /// Check if data has been received from the transport stream. + /// + /// + /// true if data has been received otherwise false. + /// + bool receiveData(); + + /// + /// Adds a byte to the protocol buffer. See also: + /// + /// + /// + /// The byte to be inserted to the buffer. + /// + void writeByte(uint8_t data); + + /// + /// Adds a integer to the protocol buffer. See also: + /// + /// + /// + /// The integer to be inserted to the buffer. + /// + void writeInt(int16_t data); + + /// + /// Retrives a byte from the protocol buffer. See also: + /// + /// + /// + /// The byte from the buffer otherwise 0 if an error occurred. + /// + uint8_t readByte(); + + /// + /// Retrives a integer from the protocol buffer. See also: + /// + /// + /// + /// The integer from the buffer otherwise 0 if an error occurred. + /// + int16_t readInt(); + +private: + void _resetData(); + + uint8_t _data[MAX_DATA_SIZE]; //data storage, for both read and send + uint8_t _position; + + Stream *_stream; + uint8_t _size; //size of data in bytes both for read and send + uint8_t _rx_array_inx; //index for RX parsing buffer + uint8_t _rx_len; //RX packet length according to the packet + uint8_t _calc_CS; //calculated Chacksum +}; + +#endif From c5b202e3703539ec0e76213002abf146aa93a555 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Thu, 9 Feb 2017 18:18:19 +0100 Subject: [PATCH 42/67] Added readme & license --- avr/libraries/AnalogMultiplexer/LICENSE.md | 23 +++++++++++++ avr/libraries/AnalogMultiplexer/README.md | 33 +++++++++++++++++++ .../AnalogMultiplexerExample.ino | 2 +- .../src/AnalogMultiplexer.cpp | 26 +++++++++++++++ .../AnalogMultiplexer/src/AnalogMultiplexer.h | 26 +++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 avr/libraries/AnalogMultiplexer/LICENSE.md create mode 100644 avr/libraries/AnalogMultiplexer/README.md diff --git a/avr/libraries/AnalogMultiplexer/LICENSE.md b/avr/libraries/AnalogMultiplexer/LICENSE.md new file mode 100644 index 0000000..0b11359 --- /dev/null +++ b/avr/libraries/AnalogMultiplexer/LICENSE.md @@ -0,0 +1,23 @@ +Library for the CMOS 40xx analog multiplexer series. + +The MIT License (MIT) + +Copyright (c) 2017 Julian Sanin + +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. diff --git a/avr/libraries/AnalogMultiplexer/README.md b/avr/libraries/AnalogMultiplexer/README.md new file mode 100644 index 0000000..576482c --- /dev/null +++ b/avr/libraries/AnalogMultiplexer/README.md @@ -0,0 +1,33 @@ +# CMOS 40xx Analog Multiplexer Series Library +A library to interface with analog multiplexers. Supports 4051, 4052, 4053, and +4067 series multiplexers. + +## How to use +``` +#include + +enum { + PIN_EN = 2, // The enable pin of the multiplexer. + PIN_S0 = 3, // Channel selector pin 0. + PIN_S1 = 4, // Channel selector pin 1. + PIN_S2 = 5, // Channel selector pin 2. + PIN_IO = A0 // Use here a pin that can work in analog & digital modes. +}; + +AMxx4051 mux{ PIN_EN, PIN_S0, PIN_S1, PIN_S2 }; // 8:1 Multiplexer. + +void setup() { + Serial.begin(9600); + while (!Serial); +} + +void loop() { + mux.pinMode(PIN_IO, INPUT); // PIN_IO will be used to read from the mux. + mux.enable(); // Enable multiplexer. + for (int channel = 0; channel < 8; channel++) { + int value = mux.analogRead(channel); // Do analog read. + Serial.println(value); + } + Serial.println(); +} +``` diff --git a/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino b/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino index 9a972b4..bb24b26 100644 --- a/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino +++ b/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino @@ -42,7 +42,7 @@ void setup() { Serial.println(value); } Serial.println(); - mux.disable(); // Switch of multiplexer. + mux.disable(); // Switch off multiplexer. } void loop() { } diff --git a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp index 3ec2d02..5a61f13 100644 --- a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp +++ b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp @@ -1,3 +1,29 @@ +/* + * Library for the CMOS 40xx analog multiplexer series. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 "AnalogMultiplexer.h" AnalogMultiplexer::AnalogMultiplexer(uint8_t pinEnable) { diff --git a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h index 4825908..469b62a 100644 --- a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h +++ b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.h @@ -1,3 +1,29 @@ +/* + * Library for the CMOS 40xx analog multiplexer series. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 ANALOG_MULTIPLEXER_H #define ANALOG_MULTIPLEXER_H From 020e706abdcb8066e1e42533b8a123eb03b20f4a Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Thu, 9 Feb 2017 18:42:23 +0100 Subject: [PATCH 43/67] Added IntegerEasyTransfer examples --- .../IntegerEasyTransfer_RX_Example.ino | 45 ++++++++++++++++++ .../IntegerEasyTransfer_TX_Example.ino | 46 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_RX_Example/IntegerEasyTransfer_RX_Example.ino create mode 100644 avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_TX_Example/IntegerEasyTransfer_TX_Example.ino diff --git a/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_RX_Example/IntegerEasyTransfer_RX_Example.ino b/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_RX_Example/IntegerEasyTransfer_RX_Example.ino new file mode 100644 index 0000000..b89777d --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_RX_Example/IntegerEasyTransfer_RX_Example.ino @@ -0,0 +1,45 @@ +#include + +//create object +IntegerEasyTransfer ET; + +struct RECEIVE_DATA_STRUCTURE { + //put your variable definitions here for the data you want to receive + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int16_t blinks; + int16_t pause; +}; + +//give a name to the group of data +RECEIVE_DATA_STRUCTURE mydata; + +void setup() { + Serial.begin(9600); + //start the library, pass in the name of the serial port. Can be Serial, + //Serial1, Serial2, etc. + ET.begin(&Serial); + + pinMode(13, OUTPUT); + +} + +void loop() { + //check and see if a data packet has come in. + if (ET.receiveData()) { + //this is how you access the variables. + //[name of the group].[variable name] + mydata.blinks = ET.readInt(); + mydata.pause = ET.readInt(); + //since we have data, we will blink it out. + for (int i = mydata.blinks; i > 0; i--) { + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + } + + //you should make this delay shorter then your transmit delay or else + //messages could be lost + delay(250); +} diff --git a/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_TX_Example/IntegerEasyTransfer_TX_Example.ino b/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_TX_Example/IntegerEasyTransfer_TX_Example.ino new file mode 100644 index 0000000..ae7e84c --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_TX_Example/IntegerEasyTransfer_TX_Example.ino @@ -0,0 +1,46 @@ +#include + +//create object +IntegerEasyTransfer ET; + +struct SEND_DATA_STRUCTURE { + //put your variable definitions here for the data you want to send + //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO + int16_t blinks; + int16_t pause; +}; + +//give a name to the group of data +SEND_DATA_STRUCTURE mydata; + +void setup() { + Serial.begin(9600); + //start the library, pass in the name of the serial port. Can be Serial, + //Serial1, Serial2, etc. + ET.begin(&Serial); + + pinMode(13, OUTPUT); + + randomSeed(analogRead(0)); + +} + +void loop() { + //this is how you access the variables. [name of the group].[variable name] + mydata.blinks = random(5); + mydata.pause = random(5); + //send the data + ET.writeInt(mydata.blinks); + ET.writeInt(mydata.pause); + ET.sendData(); + + //Just for fun, we will blink it out too + for (int i = mydata.blinks; i > 0; i--) { + digitalWrite(13, HIGH); + delay(mydata.pause * 100); + digitalWrite(13, LOW); + delay(mydata.pause * 100); + } + + delay(5000); +} From 1f3b79a7ae1dc11c940c15d2c6c6897f100cffa8 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Thu, 9 Feb 2017 20:34:45 +0100 Subject: [PATCH 44/67] Added modified platform variants for the support of the SoftwareSerial library --- avr/boards.txt | 4 +- avr/platform.txt | 2 +- avr/variants/robot_control/pins_arduino.h | 331 ++++++++++++++++++++++ avr/variants/robot_motor/pins_arduino.h | 326 +++++++++++++++++++++ 4 files changed, 660 insertions(+), 3 deletions(-) create mode 100644 avr/variants/robot_control/pins_arduino.h create mode 100644 avr/variants/robot_motor/pins_arduino.h diff --git a/avr/boards.txt b/avr/boards.txt index f87b876..f92f2d6 100644 --- a/avr/boards.txt +++ b/avr/boards.txt @@ -49,7 +49,7 @@ arduinorobot.menu.cpu.controlboard.build.pid.3=0x8038 arduinorobot.menu.cpu.controlboard.build.pid=0x8038 arduinorobot.menu.cpu.controlboard.build.usb_product="Robot Control" arduinorobot.menu.cpu.controlboard.build.board=AVR_ROBOT_CONTROL -arduinorobot.menu.cpu.controlboard.build.variant=arduino:robot_control +arduinorobot.menu.cpu.controlboard.build.variant=robot_control ## Arduino Robot Motor Board ## ------------------------------------------------- @@ -68,6 +68,6 @@ arduinorobot.menu.cpu.motorboard.build.pid.3=0x8039 arduinorobot.menu.cpu.motorboard.build.pid=0x8039 arduinorobot.menu.cpu.motorboard.build.usb_product="Robot Motor" arduinorobot.menu.cpu.motorboard.build.board=AVR_ROBOT_MOTOR -arduinorobot.menu.cpu.motorboard.build.variant=arduino:robot_motor +arduinorobot.menu.cpu.motorboard.build.variant=robot_motor ############################################################## \ No newline at end of file diff --git a/avr/platform.txt b/avr/platform.txt index ad5c394..0f32823 100644 --- a/avr/platform.txt +++ b/avr/platform.txt @@ -5,7 +5,7 @@ # - https://github.com/arduino/Arduino/wiki/Arduino-Hardware-Cores-migration-guide-from-1.0-to-1.6 name=Arduino Robot Boards -version=1.0.0 +version=1.0.2 # AVR compile variables # --------------------- diff --git a/avr/variants/robot_control/pins_arduino.h b/avr/variants/robot_control/pins_arduino.h new file mode 100644 index 0000000..4039fa6 --- /dev/null +++ b/avr/variants/robot_control/pins_arduino.h @@ -0,0 +1,331 @@ +/* + pins_arduino.h - Pin definition functions for Arduino Robot Control Board + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2913 D. Cuartielles, X. Yang (Arduino Verkstad) + Copyright (c) 2012 D. Cuartielles, N. de la Riva, I. Gallego, E. Gallego + + 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define ARDUINO_MODEL_USB_PID 0x0038 + +#define TX_RX_LED_INIT DDRD |= (1<<5), DDRB |= (1<<0) +#define TXLED0 PORTD |= (1<<5) +#define TXLED1 PORTD &= ~(1<<5) +#define RXLED0 PORTB |= (1<<0) +#define RXLED1 PORTB &= ~(1<<0) + +#define D0 TKD0 +#define D1 TKD1 +#define D2 TKD2 +#define D3 TKD3 +#define D4 TKD4 +#define D5 TKD5 + +#define PIN_SERIAL_RX (0) +#define PIN_SERIAL_TX (1) + +static const uint8_t RX = PIN_SERIAL_RX; +static const uint8_t TX = PIN_SERIAL_TX; + +#define PIN_WIRE_SDA (2) +#define PIN_WIRE_SCL (3) + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; + +// Map SPI port to 'new' pins D14..D17 +#define PIN_SPI_SS (17) +#define PIN_SPI_MOSI (16) +#define PIN_SPI_MISO (14) +#define PIN_SPI_SCK (15) + +static const uint8_t SS = PIN_SPI_SS; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +// Mapping of analog pins as digital I/O +// A6-A11 share with digital pins +#define PIN_A0 (18) +#define PIN_A1 (19) +#define PIN_A2 (20) +#define PIN_A3 (21) +#define PIN_A4 (22) +#define PIN_A5 (23) +#define PIN_A6 (24) +#define PIN_A7 (25) +#define PIN_A8 (26) +#define PIN_A9 (27) +#define PIN_A10 (28) +#define PIN_A11 (29) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; // D4 +static const uint8_t A7 = PIN_A7; // D6 +static const uint8_t A8 = PIN_A8; // D8 +static const uint8_t A9 = PIN_A9; // D9 +static const uint8_t A10 = PIN_A10; // D10 +static const uint8_t A11 = PIN_A11; // D12 + +// Specific Mapping for the Control Board +static const uint8_t KEY = 18; // AD0 +static const uint8_t MUX_IN = 24; // ADC8 - A6 +static const uint8_t MUXA = 6; // D5 - TKD4 +static const uint8_t MUXB = 11; // D11 +static const uint8_t MUXC = 12; // D12 - TKD5 +static const uint8_t MUXD = 13; // D13 +static const uint8_t BUZZ = 5; // D5 +static const uint8_t POT = 23; // AD5 +static const uint8_t DC_LCD = 10; // D10 +static const uint8_t LCD_CS = 9; // D9 +static const uint8_t RST_LCD = 7; // D6 +static const uint8_t CARD_CS = 8; // D8 +static const uint8_t TKD0 = 19; // ADC6 - A1 +static const uint8_t TKD1 = 20; // ADC5 - A2 +static const uint8_t TKD2 = 21; // ADC4 - A3 +static const uint8_t TKD3 = 22; // ADC1 - A4 +static const uint8_t TKD4 = 6; // D5 - MUXA +static const uint8_t TKD5 = 12; // D12 - MUXC +static const uint8_t LED1 = 17; // D17 - RX_Led + +#define digitalPinToPCICR(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCICR) : ((uint8_t *)0)) +#define digitalPinToPCICRbit(p) 0 +#define digitalPinToPCMSK(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCMSK0) : ((uint8_t *)0)) +#define digitalPinToPCMSKbit(p) ( ((p) >= 8 && (p) <= 11) ? (p) - 4 : ((p) == 14 ? 3 : ((p) == 15 ? 1 : ((p) == 16 ? 2 : ((p) == 17 ? 0 : (p - A8 + 4)))))) + +// __AVR_ATmega32U4__ has an unusual mapping of pins to channels +extern const uint8_t PROGMEM analog_pin_to_channel_PGM[]; +#define analogPinToChannel(P) ( pgm_read_byte( analog_pin_to_channel_PGM + (P) ) ) + +#define digitalPinToInterrupt(p) ((p) == 0 ? 2 : ((p) == 1 ? 3 : ((p) == 2 ? 1 : ((p) == 3 ? 0 : ((p) == 7 ? 4 : NOT_AN_INTERRUPT))))) + +#ifdef ARDUINO_MAIN + +// On the Arduino board, digital pins are also used +// for the analog output (software PWM). Analog input +// pins are a separate set. + +// ARDUINO LEONARDO / ARDUINO ROBOT CONTROL / ATMEGA 32U4 / FUNCTION / REGISTER +// +// D0 RX PD2 RX RXD1/INT2 +// D1 TX PD3 TX TXD1/INT3 +// D2 SDA PD1 SDA SDA/INT1 +// D3# SCL PD0 PWM8/SCL OC0B/SCL/INT0 +// D4 MUX_IN A6 PD4 ADC8 +// D5# BUZZ PC6 ??? OC3A/#OC4A +// D6# MUXA/TKD4 A7 PD7 FastPWM #OC4D/ADC10 +// D7 RST_LCD PE6 INT6/AIN0 +// +// D8 CARD_CS A8 PB4 ADC11/PCINT4 +// D9# LCD_CS A9 PB5 PWM16 OC1A/#OC4B/ADC12/PCINT5 +// D10# DC_LCD A10 PB6 PWM16 OC1B/0c4B/ADC13/PCINT6 +// D11# MUXB PB7 PWM8/16 0C0A/OC1C/#RTS/PCINT7 +// D12 MUXC/TKD5 A11 PD6 T1/#OC4D/ADC9 +// D13# MUXD PC7 PWM10 CLK0/OC4A +// +// A0 KEY D18 PF7 ADC7 +// A1 TKD0 D19 PF6 ADC6 +// A2 TKD1 D20 PF5 ADC5 +// A3 TKD2 D21 PF4 ADC4 +// A4 TKD3 D22 PF1 ADC1 +// A5 POT D23 PF0 ADC0 +// +// MISO MISO D14 PB3 MISO,PCINT3 +// SCK SCK D15 PB1 SCK,PCINT1 +// MOSI MOSI D16 PB2 MOSI,PCINT2 +// SS RX_LED D17 PB0 RXLED,SS/PCINT0 +// +// TXLED TX_LED PD5 +// HWB PE2 HWB + +// these arrays map port names (e.g. port B) to the +// appropriate addresses for various functions (e.g. reading +// and writing) +const uint16_t PROGMEM port_to_mode_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &DDRB, + (uint16_t) &DDRC, + (uint16_t) &DDRD, + (uint16_t) &DDRE, + (uint16_t) &DDRF, +}; + +const uint16_t PROGMEM port_to_output_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PORTB, + (uint16_t) &PORTC, + (uint16_t) &PORTD, + (uint16_t) &PORTE, + (uint16_t) &PORTF, +}; + +const uint16_t PROGMEM port_to_input_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PINB, + (uint16_t) &PINC, + (uint16_t) &PIND, + (uint16_t) &PINE, + (uint16_t) &PINF, +}; + +const uint8_t PROGMEM digital_pin_to_port_PGM[30] = { + PD, // D0 - PD2 + PD, // D1 - PD3 + PD, // D2 - PD1 + PD, // D3 - PD0 + PD, // D4 - PD4 + PC, // D5 - PC6 + PD, // D6 - PD7 + PE, // D7 - PE6 + + PB, // D8 - PB4 + PB, // D9 - PB5 + PB, // D10 - PB6 + PB, // D11 - PB7 + PD, // D12 - PD6 + PC, // D13 - PC7 + + PB, // D14 - MISO - PB3 + PB, // D15 - SCK - PB1 + PB, // D16 - MOSI - PB2 + PB, // D17 - SS - PB0 + + PF, // D18 - A0 - PF7 + PF, // D19 - A1 - PF6 + PF, // D20 - A2 - PF5 + PF, // D21 - A3 - PF4 + PF, // D22 - A4 - PF1 + PF, // D23 - A5 - PF0 + + PD, // D24 / D4 - A6 - PD4 + PD, // D25 / D6 - A7 - PD7 + PB, // D26 / D8 - A8 - PB4 + PB, // D27 / D9 - A9 - PB5 + PB, // D28 / D10 - A10 - PB6 + PD, // D29 / D12 - A11 - PD6 +}; + +const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[30] = { + _BV(2), // D0 - PD2 + _BV(3), // D1 - PD3 + _BV(1), // D2 - PD1 + _BV(0), // D3 - PD0 + _BV(4), // D4 - PD4 + _BV(6), // D5 - PC6 + _BV(7), // D6 - PD7 + _BV(6), // D7 - PE6 + + _BV(4), // D8 - PB4 + _BV(5), // D9 - PB5 + _BV(6), // D10 - PB6 + _BV(7), // D11 - PB7 + _BV(6), // D12 - PD6 + _BV(7), // D13 - PC7 + + _BV(3), // D14 - MISO - PB3 + _BV(1), // D15 - SCK - PB1 + _BV(2), // D16 - MOSI - PB2 + _BV(0), // D17 - SS - PB0 + + _BV(7), // D18 - A0 - PF7 + _BV(6), // D19 - A1 - PF6 + _BV(5), // D20 - A2 - PF5 + _BV(4), // D21 - A3 - PF4 + _BV(1), // D22 - A4 - PF1 + _BV(0), // D23 - A5 - PF0 + + _BV(4), // D24 / D4 - A6 - PD4 + _BV(7), // D25 / D6 - A7 - PD7 + _BV(4), // D26 / D8 - A8 - PB4 + _BV(5), // D27 / D9 - A9 - PB5 + _BV(6), // D28 / D10 - A10 - PB6 + _BV(6), // D29 / D12 - A11 - PD6 +}; + +const uint8_t PROGMEM digital_pin_to_timer_PGM[18] = { + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + TIMER0B, /* 3 */ + NOT_ON_TIMER, + TIMER3A, /* 5 */ + TIMER4D, /* 6 */ + NOT_ON_TIMER, + + NOT_ON_TIMER, + TIMER1A, /* 9 */ + TIMER1B, /* 10 */ + TIMER0A, /* 11 */ + + NOT_ON_TIMER, + TIMER4A, /* 13 */ + + NOT_ON_TIMER, + NOT_ON_TIMER, +}; + +const uint8_t PROGMEM analog_pin_to_channel_PGM[12] = { + 7, // A0 PF7 ADC7 + 6, // A1 PF6 ADC6 + 5, // A2 PF5 ADC5 + 4, // A3 PF4 ADC4 + 1, // A4 PF1 ADC1 + 0, // A5 PF0 ADC0 + 8, // A6 D4 PD4 ADC8 + 10, // A7 D6 PD7 ADC10 + 11, // A8 D8 PB4 ADC11 + 12, // A9 D9 PB5 ADC12 + 13, // A10 D10 PB6 ADC13 + 9 // A11 D12 PD6 ADC9 +}; + +#endif /* ARDUINO_MAIN */ + +// These serial port names are intended to allow libraries and architecture-neutral +// sketches to automatically default to the correct port name for a particular type +// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, +// the first hardware serial port whose RX/TX pins are not dedicated to another use. +// +// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor +// +// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial +// +// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library +// +// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. +// +// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX +// pins are NOT connected to anything by default. +#define SERIAL_PORT_MONITOR Serial +#define SERIAL_PORT_USBVIRTUAL Serial +#define SERIAL_PORT_HARDWARE Serial1 + +#endif /* Pins_Arduino_h */ diff --git a/avr/variants/robot_motor/pins_arduino.h b/avr/variants/robot_motor/pins_arduino.h new file mode 100644 index 0000000..2ed77e7 --- /dev/null +++ b/avr/variants/robot_motor/pins_arduino.h @@ -0,0 +1,326 @@ +/* + pins_arduino.h - Pin definition functions for Arduino Robot Control Board + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2913 D. Cuartielles, X. Yang (Arduino Verkstad) + Copyright (c) 2012 D. Cuartielles, N. de la Riva, I. Gallego, E. Gallego + + 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define ARDUINO_MODEL_USB_PID 0x0039 + +#define TX_RX_LED_INIT DDRD |= (1<<5), DDRB |= (1<<0) +#define TXLED0 PORTD |= (1<<5) +#define TXLED1 PORTD &= ~(1<<5) +#define RXLED0 PORTB |= (1<<0) +#define RXLED1 PORTB &= ~(1<<0) + +#define D10 TK1 +#define D9 TK2 +#define D8 TK4 +#define D7 TK3 + +#define PIN_SERIAL_RX (0) +#define PIN_SERIAL_TX (1) + +static const uint8_t RX = PIN_SERIAL_RX; +static const uint8_t TX = PIN_SERIAL_TX; + +#define PIN_WIRE_SDA (2) +#define PIN_WIRE_SCL (3) + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; + +// Map SPI port to 'new' pins D14..D17 +#define PIN_SPI_SS (17) +#define PIN_SPI_MOSI (16) +#define PIN_SPI_MISO (14) +#define PIN_SPI_SCK (15) + +static const uint8_t SS = PIN_SPI_SS; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +// Mapping of analog pins as digital I/O +// A6-A11 share with digital pins +#define PIN_A0 (18) +#define PIN_A1 (19) +#define PIN_A2 (20) +#define PIN_A3 (21) +#define PIN_A4 (22) +#define PIN_A5 (23) +#define PIN_A6 (24) +#define PIN_A7 (25) +#define PIN_A8 (26) +#define PIN_A9 (27) +#define PIN_A10 (28) +#define PIN_A11 (29) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; // D4 +static const uint8_t A7 = PIN_A7; // D6 +static const uint8_t A8 = PIN_A8; // D8 +static const uint8_t A9 = PIN_A9; // D9 +static const uint8_t A10 = PIN_A10; // D10 +static const uint8_t A11 = PIN_A11; // D12 + +// Specific Mapping for the Motor Board +static const uint8_t MUX_IN = 20; // A2 +static const uint8_t MUXA = 7; // D7 +static const uint8_t MUXB = 8; // D8 +static const uint8_t MUXC = 11; // D11 +static const uint8_t MUXI = 13; // D13 +static const uint8_t TRIM = 21; // A3 +static const uint8_t SENSE_A = 22; // A4 +static const uint8_t SENSE_B = 23; // A5 +static const uint8_t IN_A1 = 6; // D6 - A7 +static const uint8_t IN_A2 = 5; // D5 +static const uint8_t IN_B1 = 10; // D10 +static const uint8_t IN_B2 = 9; // D9 +static const uint8_t TK1 = 18; // A0 +static const uint8_t TK2 = 19; // A1 +static const uint8_t TK3 = 4; // A6 +static const uint8_t TK4 = 12; // A11 + +#define digitalPinToPCICR(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCICR) : ((uint8_t *)0)) +#define digitalPinToPCICRbit(p) 0 +#define digitalPinToPCMSK(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCMSK0) : ((uint8_t *)0)) +#define digitalPinToPCMSKbit(p) ( ((p) >= 8 && (p) <= 11) ? (p) - 4 : ((p) == 14 ? 3 : ((p) == 15 ? 1 : ((p) == 16 ? 2 : ((p) == 17 ? 0 : (p - A8 + 4)))))) + +// __AVR_ATmega32U4__ has an unusual mapping of pins to channels +extern const uint8_t PROGMEM analog_pin_to_channel_PGM[]; +#define analogPinToChannel(P) ( pgm_read_byte( analog_pin_to_channel_PGM + (P) ) ) + +#define digitalPinToInterrupt(p) ((p) == 0 ? 2 : ((p) == 1 ? 3 : ((p) == 2 ? 1 : ((p) == 3 ? 0 : ((p) == 7 ? 4 : NOT_AN_INTERRUPT))))) + +#ifdef ARDUINO_MAIN + +// On the Arduino board, digital pins are also used +// for the analog output (software PWM). Analog input +// pins are a separate set. + +// ARDUINO LEONARDO / ARDUINO ROBOT CONTROL / ATMEGA 32U4 / FUNCTION / REGISTER +// +// D0 RX PD2 RX RXD1/INT2 +// D1 TX PD3 TX TXD1/INT3 +// D2 SDA PD1 SDA SDA/INT1 +// D3# SCL PD0 PWM8/SCL OC0B/SCL/INT0 +// D4 TK3 A6 PD4 ADC8 +// D5# INA2 PC6 ??? OC3A/#OC4A +// D6# INA1 A7 PD7 FastPWM #OC4D/ADC10 +// D7 MUXA PE6 INT6/AIN0 +// +// D8 MUXB A8 PB4 ADC11/PCINT4 +// D9# INB2 A9 PB5 PWM16 OC1A/#OC4B/ADC12/PCINT5 +// D10# INB1 A10 PB6 PWM16 OC1B/0c4B/ADC13/PCINT6 +// D11# MUXC PB7 PWM8/16 0C0A/OC1C/#RTS/PCINT7 +// D12 TK4 A11 PD6 T1/#OC4D/ADC9 +// D13# MUXI PC7 PWM10 CLK0/OC4A +// +// A0 TK1 D18 PF7 ADC7 +// A1 TK2 D19 PF6 ADC6 +// A2 MUX_IN D20 PF5 ADC5 +// A3 TRIM D21 PF4 ADC4 +// A4 SENSE_A D22 PF1 ADC1 +// A5 SENSE_B D23 PF0 ADC0 +// +// MISO MISO D14 PB3 MISO,PCINT3 +// SCK SCK D15 PB1 SCK,PCINT1 +// MOSI MOSI D16 PB2 MOSI,PCINT2 +// SS RX_LED D17 PB0 RXLED,SS/PCINT0 +// +// TXLED TX_LED PD5 +// HWB PE2 HWB + +// these arrays map port names (e.g. port B) to the +// appropriate addresses for various functions (e.g. reading +// and writing) +const uint16_t PROGMEM port_to_mode_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &DDRB, + (uint16_t) &DDRC, + (uint16_t) &DDRD, + (uint16_t) &DDRE, + (uint16_t) &DDRF, +}; + +const uint16_t PROGMEM port_to_output_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PORTB, + (uint16_t) &PORTC, + (uint16_t) &PORTD, + (uint16_t) &PORTE, + (uint16_t) &PORTF, +}; + +const uint16_t PROGMEM port_to_input_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PINB, + (uint16_t) &PINC, + (uint16_t) &PIND, + (uint16_t) &PINE, + (uint16_t) &PINF, +}; + +const uint8_t PROGMEM digital_pin_to_port_PGM[30] = { + PD, // D0 - PD2 + PD, // D1 - PD3 + PD, // D2 - PD1 + PD, // D3 - PD0 + PD, // D4 - PD4 + PC, // D5 - PC6 + PD, // D6 - PD7 + PE, // D7 - PE6 + + PB, // D8 - PB4 + PB, // D9 - PB5 + PB, // D10 - PB6 + PB, // D11 - PB7 + PD, // D12 - PD6 + PC, // D13 - PC7 + + PB, // D14 - MISO - PB3 + PB, // D15 - SCK - PB1 + PB, // D16 - MOSI - PB2 + PB, // D17 - SS - PB0 + + PF, // D18 - A0 - PF7 + PF, // D19 - A1 - PF6 + PF, // D20 - A2 - PF5 + PF, // D21 - A3 - PF4 + PF, // D22 - A4 - PF1 + PF, // D23 - A5 - PF0 + + PD, // D24 / D4 - A6 - PD4 + PD, // D25 / D6 - A7 - PD7 + PB, // D26 / D8 - A8 - PB4 + PB, // D27 / D9 - A9 - PB5 + PB, // D28 / D10 - A10 - PB6 + PD, // D29 / D12 - A11 - PD6 +}; + +const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[30] = { + _BV(2), // D0 - PD2 + _BV(3), // D1 - PD3 + _BV(1), // D2 - PD1 + _BV(0), // D3 - PD0 + _BV(4), // D4 - PD4 + _BV(6), // D5 - PC6 + _BV(7), // D6 - PD7 + _BV(6), // D7 - PE6 + + _BV(4), // D8 - PB4 + _BV(5), // D9 - PB5 + _BV(6), // D10 - PB6 + _BV(7), // D11 - PB7 + _BV(6), // D12 - PD6 + _BV(7), // D13 - PC7 + + _BV(3), // D14 - MISO - PB3 + _BV(1), // D15 - SCK - PB1 + _BV(2), // D16 - MOSI - PB2 + _BV(0), // D17 - SS - PB0 + + _BV(7), // D18 - A0 - PF7 + _BV(6), // D19 - A1 - PF6 + _BV(5), // D20 - A2 - PF5 + _BV(4), // D21 - A3 - PF4 + _BV(1), // D22 - A4 - PF1 + _BV(0), // D23 - A5 - PF0 + + _BV(4), // D24 / D4 - A6 - PD4 + _BV(7), // D25 / D6 - A7 - PD7 + _BV(4), // D26 / D8 - A8 - PB4 + _BV(5), // D27 / D9 - A9 - PB5 + _BV(6), // D28 / D10 - A10 - PB6 + _BV(6), // D29 / D12 - A11 - PD6 +}; + +const uint8_t PROGMEM digital_pin_to_timer_PGM[18] = { + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + TIMER0B, /* 3 */ + NOT_ON_TIMER, + TIMER3A, /* 5 */ + TIMER4D, /* 6 */ + NOT_ON_TIMER, + + NOT_ON_TIMER, + TIMER1A, /* 9 */ + TIMER1B, /* 10 */ + TIMER0A, /* 11 */ + + NOT_ON_TIMER, + TIMER4A, /* 13 */ + + NOT_ON_TIMER, + NOT_ON_TIMER, +}; + +const uint8_t PROGMEM analog_pin_to_channel_PGM[12] = { + 7, // A0 PF7 ADC7 + 6, // A1 PF6 ADC6 + 5, // A2 PF5 ADC5 + 4, // A3 PF4 ADC4 + 1, // A4 PF1 ADC1 + 0, // A5 PF0 ADC0 + 8, // A6 D4 PD4 ADC8 + 10, // A7 D6 PD7 ADC10 + 11, // A8 D8 PB4 ADC11 + 12, // A9 D9 PB5 ADC12 + 13, // A10 D10 PB6 ADC13 + 9 // A11 D12 PD6 ADC9 +}; + +#endif /* ARDUINO_MAIN */ + +// These serial port names are intended to allow libraries and architecture-neutral +// sketches to automatically default to the correct port name for a particular type +// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, +// the first hardware serial port whose RX/TX pins are not dedicated to another use. +// +// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor +// +// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial +// +// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library +// +// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. +// +// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX +// pins are NOT connected to anything by default. +#define SERIAL_PORT_MONITOR Serial +#define SERIAL_PORT_USBVIRTUAL Serial +#define SERIAL_PORT_HARDWARE Serial1 + +#endif /* Pins_Arduino_h */ From 1710862973610759d7074ded639ec2f986fe7698 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Wed, 15 Feb 2017 21:05:28 +0100 Subject: [PATCH 45/67] Fixed 'build.board' preference warning --- avr/boards.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avr/boards.txt b/avr/boards.txt index f92f2d6..d03d168 100644 --- a/avr/boards.txt +++ b/avr/boards.txt @@ -29,6 +29,8 @@ arduinorobot.bootloader.lock_bits=0x2F arduinorobot.build.mcu=atmega32u4 arduinorobot.build.f_cpu=16000000L arduinorobot.build.core=arduino:arduino +# Default board will be overridden by the cpu menu +arduinorobot.build.board=AVR_ROBOT_CONTROL arduinorobot.build.vid=0x2341 arduinorobot.build.extra_flags={build.usb_flags} From f1b604a403935a7a24f8aca2a9ce97dfc73014cb Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Thu, 2 Mar 2017 14:02:17 +0100 Subject: [PATCH 46/67] Added new classes to support two way communications and extended messages and software reset --- avr/libraries/IntegerEasyTransfer/README.txt | 5 +- .../IntegerEasyTransfer_Interop_Example.ino | 67 +++++++++ .../IntegerEasyTransfer/keywords.txt | 16 +++ .../IntegerEasyTransfer/library.properties | 2 +- .../src/IntegerEasyTransferFeature.h | 63 +++++++++ .../src/IntegerEasyTransferFeatureManager.cpp | 81 +++++++++++ .../src/IntegerEasyTransferFeatureManager.h | 53 +++++++ .../src/TwoWayIntegerEasyTransfer.cpp | 99 +++++++++++++ .../src/TwoWayIntegerEasyTransfer.h | 132 ++++++++++++++++++ 9 files changed, 516 insertions(+), 2 deletions(-) create mode 100644 avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_Interop_Example/IntegerEasyTransfer_Interop_Example.ino create mode 100644 avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeature.h create mode 100644 avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.cpp create mode 100644 avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.h create mode 100644 avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp create mode 100644 avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h diff --git a/avr/libraries/IntegerEasyTransfer/README.txt b/avr/libraries/IntegerEasyTransfer/README.txt index 6526ee0..08dbc14 100644 --- a/avr/libraries/IntegerEasyTransfer/README.txt +++ b/avr/libraries/IntegerEasyTransfer/README.txt @@ -1,5 +1,5 @@ /******************************************************************************* -* IntegerEasyTransfer 1.0.0 library modified by Julian Sanin, sourced from: +* IntegerEasyTransfer 1.1.0 library modified by Julian Sanin, sourced from: * * EasyTransfer Arduino Library v2.1 * details and example sketch: @@ -44,6 +44,9 @@ * The library supports a maximum of 20 uint8_t or 10 int16_t values. * Mixed uint8_t and int16_t are allowed but care should be taken that the * values do not overflow the data buffer. +* 1.1.0 +* Added new classes to support two way communications and extended +* messages and software reset. * * * Limits of the Library diff --git a/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_Interop_Example/IntegerEasyTransfer_Interop_Example.ino b/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_Interop_Example/IntegerEasyTransfer_Interop_Example.ino new file mode 100644 index 0000000..8517c01 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/examples/IntegerEasyTransfer_Interop_Example/IntegerEasyTransfer_Interop_Example.ino @@ -0,0 +1,67 @@ +#include +#include + +// New custom feature. +struct MyFeature : public IntegerEasyTransferFeature { + + enum { + MY_COMMAND = 0x0F, + // Add further commands if needed. + }; + + uint8_t data = 0x00; + + bool handleMessage(uint8_t command, IntegerEasyTransfer & request) { + // Filter commands that you are interested in. + switch(command) { + case MY_COMMAND: + data = request.readByte(); + return true; + default: + // Got a message that is not handled by this feature. + return false; + } + } + + void reset() { + data = 0x00; + } + + void sendNewData() { + // Extended feature messages uses following format for sending data: + TwoWayIntegerEasyTransfer.writeByte( + TwoWayIntegerEasyTransfer.MESSAGE_FEATURE + ); + TwoWayIntegerEasyTransfer.writeByte(MY_COMMAND); + TwoWayIntegerEasyTransfer.writeByte(++data); + TwoWayIntegerEasyTransfer.sendData(); + } +}; + +// Instance of the feature. +MyFeature myFeature; + +// Create a instance of the feature manager. +IntegerEasyTransferFeatureManager featureManager; + +void setup() { + // Begin communication channel. + Serial.begin(9600); + TwoWayIntegerEasyTransfer.begin(&Serial); + // Attach callbacks and add features. + TwoWayIntegerEasyTransfer.attach([]() { doSystemReset(); }); + featureManager.addFeature(myFeature); + // Perform software reset manually. + doSystemReset(); +} + +void loop() { + // Keep message pumping up and running. + if (TwoWayIntegerEasyTransfer.hasReceivedData()) { + TwoWayIntegerEasyTransfer.processInput(); + } +} + +void doSystemReset() { + featureManager.reset(); +} \ No newline at end of file diff --git a/avr/libraries/IntegerEasyTransfer/keywords.txt b/avr/libraries/IntegerEasyTransfer/keywords.txt index 56352c6..2592c87 100644 --- a/avr/libraries/IntegerEasyTransfer/keywords.txt +++ b/avr/libraries/IntegerEasyTransfer/keywords.txt @@ -8,6 +8,12 @@ ####################################### IntegerEasyTransfer KEYWORD1 +IntegerEasyTransferFeature KEYWORD1 +IntegerEasyTransferFeatureManager KEYWORD1 +TwoWayIntegerEasyTransfer KEYWORD1 +DefaultMessageCallbackFunction KEYWORD1 +FeatureMessageCallbackFunction KEYWORD1 +SystemResetCallbackFunction KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -20,7 +26,17 @@ writeByte KEYWORD2 writeInt KEYWORD2 readByte KEYWORD2 readInt KEYWORD2 +sendSystemReset KEYWORD2 +hasReceivedData KEYWORD2 +processInput KEYWORD2 +attach KEYWORD2 +addFeature KEYWORD2 +handleMessage KEYWORD2 +reset KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### + +MESSAGE_FEATURE LITERAL1 +MESSAGE_SYSTEM_RESET LITERAL1 \ No newline at end of file diff --git a/avr/libraries/IntegerEasyTransfer/library.properties b/avr/libraries/IntegerEasyTransfer/library.properties index 5d6335c..710b79a 100644 --- a/avr/libraries/IntegerEasyTransfer/library.properties +++ b/avr/libraries/IntegerEasyTransfer/library.properties @@ -1,5 +1,5 @@ name=IntegerEasyTransfer -version=1.0.0 +version=1.1.0 author=Bill Porter , Julian Sanin maintainer=Julian Sanin sentence=A library to interface Arduino boards. diff --git a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeature.h b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeature.h new file mode 100644 index 0000000..a4bbb11 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeature.h @@ -0,0 +1,63 @@ +/* + * Library for configurable and easy transfers. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 INTEGER_EASY_TRANSFER_FEATURE_H +#define INTEGER_EASY_TRANSFER_FEATURE_H + +#include + +class IntegerEasyTransfer; + +class IntegerEasyTransferFeature { + + // Link to next feature item, used by FeatureManager. + IntegerEasyTransferFeature * _nextFeature = nullptr; + friend class IntegerEasyTransferFeatureManager; + +public: + /// + /// Handle the incoming request message. + /// + /// + /// The first command byte of the incoming request message. + /// + /// + /// Further data messages of the incoming request. + /// + /// + /// True if the message has been handled otherwise false. + /// + virtual bool handleMessage( + uint8_t command, IntegerEasyTransfer & request + ) = 0; + + /// + /// System reset command. + /// + virtual void reset() = 0; +}; + +#endif // INTEGER_EASY_TRANSFER_FEATURE_H diff --git a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.cpp b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.cpp new file mode 100644 index 0000000..9fd1ec3 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.cpp @@ -0,0 +1,81 @@ +/* + * Library for configurable and easy transfers. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 "IntegerEasyTransferFeatureManager.h" +#include "TwoWayIntegerEasyTransfer.h" + +static IntegerEasyTransferFeatureManager * _featureManager = nullptr; + +IntegerEasyTransferFeatureManager::IntegerEasyTransferFeatureManager() + : _features{ nullptr } { + _featureManager = this; + // Handle extended messages on connected features. + TwoWayIntegerEasyTransfer.attach( + [](IntegerEasyTransfer & request) { + if (_featureManager != nullptr) { + uint8_t extendedCommand = request.readByte(); + if (!_featureManager->handleMessage(extendedCommand, request)) { + // TODO: Report unhandled message. + } + } + }); +} + +bool IntegerEasyTransferFeatureManager::handleMessage( + uint8_t command, IntegerEasyTransfer & request +) { + IntegerEasyTransferFeature * selectedFeature = _features; + while (selectedFeature != nullptr) { + if (selectedFeature->handleMessage(command, request)) { + return true; + } + selectedFeature = selectedFeature->_nextFeature; + } + return false; +} + +void IntegerEasyTransferFeatureManager::reset() { + IntegerEasyTransferFeature * selectedFeature = _features; + while (selectedFeature != nullptr) { + selectedFeature->reset(); + selectedFeature = selectedFeature->_nextFeature; + } +} + +void IntegerEasyTransferFeatureManager::addFeature( + IntegerEasyTransferFeature & feature) { + if (_features != nullptr) { + IntegerEasyTransferFeature * selectedFeature = _features; + while (selectedFeature->_nextFeature != nullptr) { + selectedFeature = selectedFeature->_nextFeature; + } + selectedFeature->_nextFeature = &feature; + selectedFeature->_nextFeature = nullptr; + } else { + _features = &feature; + _features->_nextFeature = nullptr; + } +} diff --git a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.h b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.h new file mode 100644 index 0000000..e96584f --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransferFeatureManager.h @@ -0,0 +1,53 @@ +/* + * Library for configurable and easy transfers. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 INTEGER_EASY_TRANSFER_FEATURE_MANAGER_H +#define INTEGER_EASY_TRANSFER_FEATURE_MANAGER_H + +#include "IntegerEasyTransferFeature.h" + +class IntegerEasyTransferFeatureManager : public IntegerEasyTransferFeature { + + // List of managed features. + IntegerEasyTransferFeature * _features; + +public: + IntegerEasyTransferFeatureManager(); + + bool handleMessage(uint8_t command, IntegerEasyTransfer & request); + void reset(); + + /// + /// Adds the feature to the manager. See also: + /// + /// + /// + /// The feature to be added. + /// + void addFeature(IntegerEasyTransferFeature & feature); +}; + +#endif // INTEGER_EASY_TRANSFER_FEATURE_MANAGER_H diff --git a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp new file mode 100644 index 0000000..1c238c7 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp @@ -0,0 +1,99 @@ +/* + * Library for configurable and easy transfers. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 "TwoWayIntegerEasyTransfer.h" + +void CTwoWayIntegerEasyTransfer::begin(Stream * stream) { + _request.begin(stream); + _response.begin(stream); +} + +void CTwoWayIntegerEasyTransfer::sendSystemReset(void) { + // Message format: [SYSTEM_RESET] + writeByte(TwoWayIntegerEasyTransfer.MESSAGE_SYSTEM_RESET); + sendData(); +} + +void CTwoWayIntegerEasyTransfer::sendData() { + _response.sendData(); +} + +void CTwoWayIntegerEasyTransfer::writeByte(uint8_t data) { + _response.writeByte(data); +} + +void CTwoWayIntegerEasyTransfer::writeInt(int16_t data) { + _response.writeInt(data); +} + +bool CTwoWayIntegerEasyTransfer::hasReceivedData(void) { + return _request.receiveData(); +} + +void CTwoWayIntegerEasyTransfer::processInput(void) { + uint8_t command = _request.readByte(); + switch (command) { + case MESSAGE_SYSTEM_RESET: + // Extended system reset callback (0xFF). + if (_systemResetCallbackFunction != nullptr) { + _systemResetCallbackFunction(); + } + break; + case MESSAGE_FEATURE: + // Extended feature message callback (0xF0). + if (_featureMessageCallbackFunction != nullptr) { + _featureMessageCallbackFunction(_request); + } + break; + default: + // Default message callback for commands less than 0xF0. + if (_defaultMessageCallbackFunction != nullptr) { + _defaultMessageCallbackFunction(command, _request); + } + break; + } +} + +void CTwoWayIntegerEasyTransfer::attach( + DefaultMessageCallbackFunction defaultMessageCallbackFunction +) { + _defaultMessageCallbackFunction = defaultMessageCallbackFunction; +} + +void CTwoWayIntegerEasyTransfer::attach( + FeatureMessageCallbackFunction featureMessageCallbackFunction +) { + _featureMessageCallbackFunction = featureMessageCallbackFunction; +} + +void CTwoWayIntegerEasyTransfer::attach( + SystemResetCallbackFunction systemResetCallbackFunction +) { + _systemResetCallbackFunction = systemResetCallbackFunction; +} + +// Make one instance for the user to use. +CTwoWayIntegerEasyTransfer TwoWayIntegerEasyTransfer; diff --git a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h new file mode 100644 index 0000000..f7ceac0 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h @@ -0,0 +1,132 @@ +/* + * Library for configurable and easy transfers. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 TWO_WAY_INTEGER_EASY_TRANSFER_H +#define TWO_WAY_INTEGER_EASY_TRANSFER_H + +#include +#include "IntegerEasyTransfer.h" + +class CTwoWayIntegerEasyTransfer { + + using DefaultMessageCallbackFunction = + void(*)(uint8_t command, IntegerEasyTransfer & request); + using FeatureMessageCallbackFunction = + void(*)(IntegerEasyTransfer & request); + using SystemResetCallbackFunction = void(*)(void); + + DefaultMessageCallbackFunction _defaultMessageCallbackFunction = nullptr; + FeatureMessageCallbackFunction _featureMessageCallbackFunction = nullptr; + SystemResetCallbackFunction _systemResetCallbackFunction = nullptr; + + IntegerEasyTransfer _request; + IntegerEasyTransfer _response; + +public: + enum { + /// Extended message for a feature request. + MESSAGE_FEATURE = 0xF0, + /// Extended message for software reset. + MESSAGE_SYSTEM_RESET = 0xFF + }; + + /// + /// Start protocol with given transport stream. + /// + /// + /// The reference to the transport stream implementation. + /// + void begin(Stream * stream); + + /// Broadcasts a extended system reset message. + void sendSystemReset(void); + + /// + /// Sends out data in binary, with header, length info and checksum. + /// For extended feature messages send in following format: + /// { [MESSAGE_FEATURE]:byte, [EXTENDED_COMMAND]:byte, [PAYLOAD]:byte|int } + /// For default messages omit the MESSAGE_FEATURE prefix. + /// + void sendData(void); + + /// + /// Adds a byte to the protocol buffer. See also: + /// + /// + /// + /// The byte to be inserted to the buffer. + /// + void writeByte(uint8_t data); + + /// + /// Adds a integer to the protocol buffer. See also: + /// + /// + /// + /// The integer to be inserted to the buffer. + /// + void writeInt(int16_t data); + + /// Check wether messages have been received. + /// + /// True if a messages has been recieved otherwise false. + /// + bool hasReceivedData(void); + + /// Process messages after data has been received. + void processInput(void); + + /// + /// Connects callback function to listen for default messages. + /// + /// + /// The default message callback function of type + /// void(*)(uint8_t command, IntegerEasyTransfer & request) or nullptr. + /// + void attach(DefaultMessageCallbackFunction defaultMessageCallbackFunction); + + /// + /// Connects callback function to listen for extended feature messages. + /// + /// + /// The feature message callback function of type + /// void(*)(IntegerEasyTransfer & request) or a nullptr. + /// + void attach(FeatureMessageCallbackFunction featureMessageCallbackFunction); + + /// + /// Connects callback function to listen for system reset messages. + /// + /// + /// The system reset callback function of type void(*)(void) or a nullptr. + /// + void attach(SystemResetCallbackFunction systemResetCallbackFunction); +}; + +extern CTwoWayIntegerEasyTransfer TwoWayIntegerEasyTransfer; + +#endif + From 660257b152450c97c19a93f8c849f77b38e72e48 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 4 Mar 2017 08:47:04 +0100 Subject: [PATCH 47/67] Preparing merge of Robot_Motor library --- README.adoc => avr/libraries/LottieLemon/README.adoc | 0 .../examples}/Robot_IR_Array_Test/Robot_IR_Array_Test.ino | 0 .../LottieLemon/examples}/Robot_Motor_Core/Robot_Motor_Core.ino | 0 .../libraries/LottieLemon/library.properties | 0 {src => avr/libraries/LottieLemon/src}/ArduinoRobotMotorBoard.cpp | 0 {src => avr/libraries/LottieLemon/src}/ArduinoRobotMotorBoard.h | 0 {src => avr/libraries/LottieLemon/src}/EasyTransfer2.cpp | 0 {src => avr/libraries/LottieLemon/src}/EasyTransfer2.h | 0 {src => avr/libraries/LottieLemon/src}/LineFollow.h | 0 {src => avr/libraries/LottieLemon/src}/Multiplexer.cpp | 0 {src => avr/libraries/LottieLemon/src}/Multiplexer.h | 0 {src => avr/libraries/LottieLemon/src}/lineFollow.cpp | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename README.adoc => avr/libraries/LottieLemon/README.adoc (100%) rename {examples => avr/libraries/LottieLemon/examples}/Robot_IR_Array_Test/Robot_IR_Array_Test.ino (100%) rename {examples => avr/libraries/LottieLemon/examples}/Robot_Motor_Core/Robot_Motor_Core.ino (100%) rename library.properties => avr/libraries/LottieLemon/library.properties (100%) rename {src => avr/libraries/LottieLemon/src}/ArduinoRobotMotorBoard.cpp (100%) rename {src => avr/libraries/LottieLemon/src}/ArduinoRobotMotorBoard.h (100%) rename {src => avr/libraries/LottieLemon/src}/EasyTransfer2.cpp (100%) rename {src => avr/libraries/LottieLemon/src}/EasyTransfer2.h (100%) rename {src => avr/libraries/LottieLemon/src}/LineFollow.h (100%) rename {src => avr/libraries/LottieLemon/src}/Multiplexer.cpp (100%) rename {src => avr/libraries/LottieLemon/src}/Multiplexer.h (100%) rename {src => avr/libraries/LottieLemon/src}/lineFollow.cpp (100%) diff --git a/README.adoc b/avr/libraries/LottieLemon/README.adoc similarity index 100% rename from README.adoc rename to avr/libraries/LottieLemon/README.adoc diff --git a/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino b/avr/libraries/LottieLemon/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino similarity index 100% rename from examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino rename to avr/libraries/LottieLemon/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino diff --git a/examples/Robot_Motor_Core/Robot_Motor_Core.ino b/avr/libraries/LottieLemon/examples/Robot_Motor_Core/Robot_Motor_Core.ino similarity index 100% rename from examples/Robot_Motor_Core/Robot_Motor_Core.ino rename to avr/libraries/LottieLemon/examples/Robot_Motor_Core/Robot_Motor_Core.ino diff --git a/library.properties b/avr/libraries/LottieLemon/library.properties similarity index 100% rename from library.properties rename to avr/libraries/LottieLemon/library.properties diff --git a/src/ArduinoRobotMotorBoard.cpp b/avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.cpp similarity index 100% rename from src/ArduinoRobotMotorBoard.cpp rename to avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.cpp diff --git a/src/ArduinoRobotMotorBoard.h b/avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.h similarity index 100% rename from src/ArduinoRobotMotorBoard.h rename to avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.h diff --git a/src/EasyTransfer2.cpp b/avr/libraries/LottieLemon/src/EasyTransfer2.cpp similarity index 100% rename from src/EasyTransfer2.cpp rename to avr/libraries/LottieLemon/src/EasyTransfer2.cpp diff --git a/src/EasyTransfer2.h b/avr/libraries/LottieLemon/src/EasyTransfer2.h similarity index 100% rename from src/EasyTransfer2.h rename to avr/libraries/LottieLemon/src/EasyTransfer2.h diff --git a/src/LineFollow.h b/avr/libraries/LottieLemon/src/LineFollow.h similarity index 100% rename from src/LineFollow.h rename to avr/libraries/LottieLemon/src/LineFollow.h diff --git a/src/Multiplexer.cpp b/avr/libraries/LottieLemon/src/Multiplexer.cpp similarity index 100% rename from src/Multiplexer.cpp rename to avr/libraries/LottieLemon/src/Multiplexer.cpp diff --git a/src/Multiplexer.h b/avr/libraries/LottieLemon/src/Multiplexer.h similarity index 100% rename from src/Multiplexer.h rename to avr/libraries/LottieLemon/src/Multiplexer.h diff --git a/src/lineFollow.cpp b/avr/libraries/LottieLemon/src/lineFollow.cpp similarity index 100% rename from src/lineFollow.cpp rename to avr/libraries/LottieLemon/src/lineFollow.cpp From e5265791fd93d214b981accd89a9013fc46098c3 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sun, 5 Mar 2017 13:27:54 +0100 Subject: [PATCH 48/67] Moved legacy Robot_Motor library files to utility folder --- .../LottieLemon/src/{ => utility}/ArduinoRobotMotorBoard.cpp | 0 .../LottieLemon/src/{ => utility}/ArduinoRobotMotorBoard.h | 0 avr/libraries/LottieLemon/src/{ => utility}/EasyTransfer2.cpp | 0 avr/libraries/LottieLemon/src/{ => utility}/EasyTransfer2.h | 0 avr/libraries/LottieLemon/src/{ => utility}/LineFollow.h | 0 avr/libraries/LottieLemon/src/{ => utility}/Multiplexer.cpp | 0 avr/libraries/LottieLemon/src/{ => utility}/Multiplexer.h | 0 avr/libraries/LottieLemon/src/{ => utility}/lineFollow.cpp | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename avr/libraries/LottieLemon/src/{ => utility}/ArduinoRobotMotorBoard.cpp (100%) rename avr/libraries/LottieLemon/src/{ => utility}/ArduinoRobotMotorBoard.h (100%) rename avr/libraries/LottieLemon/src/{ => utility}/EasyTransfer2.cpp (100%) rename avr/libraries/LottieLemon/src/{ => utility}/EasyTransfer2.h (100%) rename avr/libraries/LottieLemon/src/{ => utility}/LineFollow.h (100%) rename avr/libraries/LottieLemon/src/{ => utility}/Multiplexer.cpp (100%) rename avr/libraries/LottieLemon/src/{ => utility}/Multiplexer.h (100%) rename avr/libraries/LottieLemon/src/{ => utility}/lineFollow.cpp (100%) diff --git a/avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.cpp similarity index 100% rename from avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.cpp rename to avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.cpp diff --git a/avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.h b/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.h similarity index 100% rename from avr/libraries/LottieLemon/src/ArduinoRobotMotorBoard.h rename to avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.h diff --git a/avr/libraries/LottieLemon/src/EasyTransfer2.cpp b/avr/libraries/LottieLemon/src/utility/EasyTransfer2.cpp similarity index 100% rename from avr/libraries/LottieLemon/src/EasyTransfer2.cpp rename to avr/libraries/LottieLemon/src/utility/EasyTransfer2.cpp diff --git a/avr/libraries/LottieLemon/src/EasyTransfer2.h b/avr/libraries/LottieLemon/src/utility/EasyTransfer2.h similarity index 100% rename from avr/libraries/LottieLemon/src/EasyTransfer2.h rename to avr/libraries/LottieLemon/src/utility/EasyTransfer2.h diff --git a/avr/libraries/LottieLemon/src/LineFollow.h b/avr/libraries/LottieLemon/src/utility/LineFollow.h similarity index 100% rename from avr/libraries/LottieLemon/src/LineFollow.h rename to avr/libraries/LottieLemon/src/utility/LineFollow.h diff --git a/avr/libraries/LottieLemon/src/Multiplexer.cpp b/avr/libraries/LottieLemon/src/utility/Multiplexer.cpp similarity index 100% rename from avr/libraries/LottieLemon/src/Multiplexer.cpp rename to avr/libraries/LottieLemon/src/utility/Multiplexer.cpp diff --git a/avr/libraries/LottieLemon/src/Multiplexer.h b/avr/libraries/LottieLemon/src/utility/Multiplexer.h similarity index 100% rename from avr/libraries/LottieLemon/src/Multiplexer.h rename to avr/libraries/LottieLemon/src/utility/Multiplexer.h diff --git a/avr/libraries/LottieLemon/src/lineFollow.cpp b/avr/libraries/LottieLemon/src/utility/lineFollow.cpp similarity index 100% rename from avr/libraries/LottieLemon/src/lineFollow.cpp rename to avr/libraries/LottieLemon/src/utility/lineFollow.cpp From d49a22c56d29738f3ce06f58c13dedd4286f8a92 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sun, 5 Mar 2017 15:08:19 +0100 Subject: [PATCH 49/67] Imported legacy Robot_Motor library to new LottieLemon library --- .../LottieLemon/{README.adoc => README.md} | 16 +- .../Robot_IR_Array_Test.ino | 26 -- .../Robot_Motor_Core/Robot_Motor_Core.ino | 21 +- .../Robot_Motor_IR_Array_Test.ino | 32 ++ avr/libraries/LottieLemon/keywords.txt | 30 ++ avr/libraries/LottieLemon/library.properties | 14 +- avr/libraries/LottieLemon/src/LottieLemon.h | 97 ++++++ .../src/utility/ArduinoRobotMotorBoard.cpp | 287 ---------------- .../src/utility/ArduinoRobotMotorBoard.h | 144 -------- .../LottieLemon/src/utility/EasyTransfer2.cpp | 176 ---------- .../LottieLemon/src/utility/EasyTransfer2.h | 76 ----- .../src/utility/LottieLemonLineFollow.cpp | 170 ++++++++++ .../{LineFollow.h => LottieLemonLineFollow.h} | 37 +-- .../src/utility/LottieLemonMotorBoard.cpp | 309 ++++++++++++++++++ .../src/utility/LottieLemonMotorBoard.h | 77 +++++ .../LottieLemon/src/utility/Multiplexer.cpp | 55 ---- .../LottieLemon/src/utility/Multiplexer.h | 42 --- .../LottieLemon/src/utility/lineFollow.cpp | 170 ---------- 18 files changed, 767 insertions(+), 1012 deletions(-) rename avr/libraries/LottieLemon/{README.adoc => README.md} (72%) delete mode 100644 avr/libraries/LottieLemon/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino create mode 100644 avr/libraries/LottieLemon/examples/Robot_Motor_IR_Array_Test/Robot_Motor_IR_Array_Test.ino create mode 100644 avr/libraries/LottieLemon/keywords.txt create mode 100644 avr/libraries/LottieLemon/src/LottieLemon.h delete mode 100644 avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.cpp delete mode 100644 avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.h delete mode 100644 avr/libraries/LottieLemon/src/utility/EasyTransfer2.cpp delete mode 100644 avr/libraries/LottieLemon/src/utility/EasyTransfer2.h create mode 100644 avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp rename avr/libraries/LottieLemon/src/utility/{LineFollow.h => LottieLemonLineFollow.h} (75%) create mode 100644 avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp create mode 100644 avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h delete mode 100644 avr/libraries/LottieLemon/src/utility/Multiplexer.cpp delete mode 100644 avr/libraries/LottieLemon/src/utility/Multiplexer.h delete mode 100644 avr/libraries/LottieLemon/src/utility/lineFollow.cpp diff --git a/avr/libraries/LottieLemon/README.adoc b/avr/libraries/LottieLemon/README.md similarity index 72% rename from avr/libraries/LottieLemon/README.adoc rename to avr/libraries/LottieLemon/README.md index 480fa52..b766357 100644 --- a/avr/libraries/LottieLemon/README.adoc +++ b/avr/libraries/LottieLemon/README.md @@ -1,12 +1,15 @@ -= Robot Motor Library for Arduino = +# Arduino Robot Library (codename 'Lottie Lemon') -The Robot has a number of built in sensors and actuators. The library is designed to easily access the robot's functionality. +The Robot has a number of built in sensors and actuators. The library is +designed to easily access the robot's functionality. -For more information about this library please visit us at -http://www.arduino.cc/en/Reference/RobotLibrary - -== License == +For more information about this library please visit: +* https://www.arduino.cc/en/Main/Robot +* https://www.arduino.cc/en/Reference/RobotLibrary +* https://github.com/j54n1n/arduinorobot +## License +``` Copyright (c) Arduino LLC. All right reserved. This library is free software; you can redistribute it and/or @@ -22,3 +25,4 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +``` \ No newline at end of file diff --git a/avr/libraries/LottieLemon/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino b/avr/libraries/LottieLemon/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino deleted file mode 100644 index be19bc6..0000000 --- a/avr/libraries/LottieLemon/examples/Robot_IR_Array_Test/Robot_IR_Array_Test.ino +++ /dev/null @@ -1,26 +0,0 @@ -/* Motor Board IR Array Test - - This example of the Arduno robot's motor board returns the - values read fron the 5 infrared sendors on the bottom of - the robot. - -*/ -// include the motor board header -#include - -String bar; // string for storing the informaton - -void setup() { - // start serial communication - Serial.begin(9600); - // initialize the library - RobotMotor.begin(); -} -void loop() { - bar = String(""); // empty the string - // read the sensors and add them to the string - bar = bar + RobotMotor.IRread(1) + ' ' + RobotMotor.IRread(2) + ' ' + RobotMotor.IRread(3) + ' ' + RobotMotor.IRread(4) + ' ' + RobotMotor.IRread(5); - // print out the values - Serial.println(bar); - delay(100); -} diff --git a/avr/libraries/LottieLemon/examples/Robot_Motor_Core/Robot_Motor_Core.ino b/avr/libraries/LottieLemon/examples/Robot_Motor_Core/Robot_Motor_Core.ino index e7911d8..5f6e29b 100644 --- a/avr/libraries/LottieLemon/examples/Robot_Motor_Core/Robot_Motor_Core.ino +++ b/avr/libraries/LottieLemon/examples/Robot_Motor_Core/Robot_Motor_Core.ino @@ -7,12 +7,25 @@ */ -#include +#include +#include + +LottieLemon::MotorBoard motorBoard; void setup() { - RobotMotor.begin(); + Serial1.begin(9600); + TwoWayIntegerEasyTransfer.begin(&Serial1); + TwoWayIntegerEasyTransfer.attach([]() { doSystemReset(); }); + doSystemReset(); } + void loop() { - RobotMotor.parseCommand(); - RobotMotor.process(); + if (TwoWayIntegerEasyTransfer.hasReceivedData()) { + TwoWayIntegerEasyTransfer.processInput(); + } + motorBoard.run(); +} + +void doSystemReset() { + motorBoard.reset(); } diff --git a/avr/libraries/LottieLemon/examples/Robot_Motor_IR_Array_Test/Robot_Motor_IR_Array_Test.ino b/avr/libraries/LottieLemon/examples/Robot_Motor_IR_Array_Test/Robot_Motor_IR_Array_Test.ino new file mode 100644 index 0000000..9d1353b --- /dev/null +++ b/avr/libraries/LottieLemon/examples/Robot_Motor_IR_Array_Test/Robot_Motor_IR_Array_Test.ino @@ -0,0 +1,32 @@ +/* Motor Board IR Array Test + +This example of the Arduno robot's motor board returns the +values read fron the 5 infrared sendors on the bottom of +the robot. + +*/ + +#include + +LottieLemon::MotorBoard motorBoard; +String bar; // string for storing the information + +void setup() { + // start serial communication + Serial.begin(9600); + // initialize the library + motorBoard.begin(); +} + +void loop() { + bar = String(""); // empty the string + // read the sensors and add them to the string + bar = bar + motorBoard.IRread(1) + + '\t' + motorBoard.IRread(2) + + '\t' + motorBoard.IRread(3) + + '\t' + motorBoard.IRread(4) + + '\t' + motorBoard.IRread(5); + // print out the values + Serial.println(bar); + delay(100); +} diff --git a/avr/libraries/LottieLemon/keywords.txt b/avr/libraries/LottieLemon/keywords.txt new file mode 100644 index 0000000..da16417 --- /dev/null +++ b/avr/libraries/LottieLemon/keywords.txt @@ -0,0 +1,30 @@ +####################################### +# Syntax Coloring Map for +# LottieLemon +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LottieLemon KEYWORD1 +MotorBoard KEYWORD1 +LineFollow KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +run KEYWORD2 +motorsWrite KEYWORD2 +IRread KEYWORD2 +calibIRs KEYWORD2 +runLineFollow KEYWORD2 +config KEYWORD2 +motorsWritePct KEYWORD2 +motorsStop KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### \ No newline at end of file diff --git a/avr/libraries/LottieLemon/library.properties b/avr/libraries/LottieLemon/library.properties index 7e697df..12c320b 100644 --- a/avr/libraries/LottieLemon/library.properties +++ b/avr/libraries/LottieLemon/library.properties @@ -1,9 +1,9 @@ -name=Robot Motor -version=1.0.2 -author=Arduino -maintainer=Arduino -sentence=Enables easy access to the motors of the Arduino Robot Motor board. For Arduino Robot only. -paragraph= +name=LottieLemon +version=1.0.0 +author=Arduino , Julian Sanin +maintainer=Julian Sanin +sentence=Enables access to the Arduino Robot boards. For Arduino Robot only. +paragraph=Supports Arduino Robot Control and Motor Board. category=Device Control -url=http://www.arduino.cc/en/Reference/RobotLibrary +url=https://github.com/j54n1n/arduinorobot architectures=avr diff --git a/avr/libraries/LottieLemon/src/LottieLemon.h b/avr/libraries/LottieLemon/src/LottieLemon.h new file mode 100644 index 0000000..b627d5f --- /dev/null +++ b/avr/libraries/LottieLemon/src/LottieLemon.h @@ -0,0 +1,97 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef LOTTIE_LEMON_H +#define LOTTIE_LEMON_H + +#include "utility/LottieLemonMotorBoard.h" + +namespace LottieLemon { + + //Command code + enum { + COMMAND_SWITCH_MODE = 0, + COMMAND_RUN = 10, + COMMAND_MOTORS_STOP = 11, + COMMAND_ANALOG_WRITE = 20, + COMMAND_DIGITAL_WRITE = 30, + COMMAND_ANALOG_READ = 40, + COMMAND_ANALOG_READ_RE = 41, + COMMAND_DIGITAL_READ = 50, + COMMAND_DIGITAL_READ_RE = 51, + COMMAND_READ_IR = 60, + COMMAND_READ_IR_RE = 61, + COMMAND_ACTION_DONE = 70, + COMMAND_READ_TRIM = 80, + COMMAND_READ_TRIM_RE = 81, + COMMAND_PAUSE_MODE = 90, + COMMAND_LINE_FOLLOW_CONFIG = 100 + }; + + + //component codename + enum { + CN_LEFT_MOTOR = 0, + CN_RIGHT_MOTOR = 1, + CN_IR = 2 + }; + + //motor board modes + enum { + MODE_SIMPLE = 0, + MODE_LINE_FOLLOW = 1, + MODE_ADJUST_MOTOR = 2, + MODE_IR_CONTROL = 3 + }; + + //bottom TKs, just for communication purpose + enum { + B_TK1 = 201, + B_TK2 = 202, + B_TK3 = 203, + B_TK4 = 204 + }; + + /* + A message structure will be: + switch mode (2): + byte COMMAND_SWITCH_MODE, byte mode + run (5): + byte COMMAND_RUN, int speedL, int speedR + analogWrite (3): + byte COMMAND_ANALOG_WRITE, byte codename, byte value; + digitalWrite (3): + byte COMMAND_DIGITAL_WRITE, byte codename, byte value; + analogRead (2): + byte COMMAND_ANALOG_READ, byte codename; + analogRead _return_ (4): + byte COMMAND_ANALOG_READ_RE, byte codename, int value; + digitalRead (2): + byte COMMAND_DIGITAL_READ, byte codename; + digitalRead _return_ (4): + byte COMMAND_DIGITAL_READ_RE, byte codename, int value; + read IR (1): + byte COMMAND_READ_IR; + read IR _return_ (9): + byte COMMAND_READ_IR_RE, int valueA, int valueB, int valueC, int valueD; + + + */ +} + +#endif // LOTTIE_LEMON_H diff --git a/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.cpp deleted file mode 100644 index 1a68b77..0000000 --- a/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - Copyright (c) 2012 Arduino LLC. All right 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. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "ArduinoRobotMotorBoard.h" -#include "EasyTransfer2.h" -#include "Multiplexer.h" -#include "LineFollow.h" - -RobotMotorBoard::RobotMotorBoard(){ - //LineFollow::LineFollow(); -} -/*void RobotMotorBoard::beginIRReceiver(){ - IRrecv::enableIRIn(); -}*/ -void RobotMotorBoard::begin(){ - //initialze communication - Serial1.begin(9600); - messageIn.begin(&Serial1); - messageOut.begin(&Serial1); - - //init MUX - uint8_t MuxPins[]={MUXA,MUXB,MUXC}; - this->IRs.begin(MuxPins,MUX_IN,3); - pinMode(MUXI,INPUT); - digitalWrite(MUXI,LOW); - - isPaused=false; -} - -void RobotMotorBoard::process(){ - if(isPaused)return;//skip process if the mode is paused - - if(mode==MODE_SIMPLE){ - //Serial.println("s"); - //do nothing? Simple mode is just about getting commands - }else if(mode==MODE_LINE_FOLLOW){ - //do line following stuff here. - LineFollow::runLineFollow(); - }else if(mode==MODE_ADJUST_MOTOR){ - //Serial.println('a'); - //motorAdjustment=analogRead(POT); - //setSpeed(255,255); - //delay(100); - } -} -void RobotMotorBoard::pauseMode(bool onOff){ - if(onOff){ - isPaused=true; - }else{ - isPaused=false; - } - stopCurrentActions(); - -} -void RobotMotorBoard::parseCommand(){ - uint8_t modeName; - uint8_t codename; - int value; - int speedL; - int speedR; - if(this->messageIn.receiveData()){ - //Serial.println("data received"); - uint8_t command=messageIn.readByte(); - //Serial.println(command); - switch(command){ - case COMMAND_SWITCH_MODE: - modeName=messageIn.readByte(); - setMode(modeName); - break; - case COMMAND_RUN: - if(mode==MODE_LINE_FOLLOW)break;//in follow line mode, the motor does not follow commands - speedL=messageIn.readInt(); - speedR=messageIn.readInt(); - motorsWrite(speedL,speedR); - break; - case COMMAND_MOTORS_STOP: - motorsStop(); - break; - case COMMAND_ANALOG_WRITE: - codename=messageIn.readByte(); - value=messageIn.readInt(); - _analogWrite(codename,value); - break; - case COMMAND_DIGITAL_WRITE: - codename=messageIn.readByte(); - value=messageIn.readByte(); - _digitalWrite(codename,value); - break; - case COMMAND_ANALOG_READ: - codename=messageIn.readByte(); - _analogRead(codename); - break; - case COMMAND_DIGITAL_READ: - codename=messageIn.readByte(); - _digitalRead(codename); - break; - case COMMAND_READ_IR: - _readIR(); - break; - case COMMAND_READ_TRIM: - _readTrim(); - break; - case COMMAND_PAUSE_MODE: - pauseMode(messageIn.readByte());//onOff state - break; - case COMMAND_LINE_FOLLOW_CONFIG: - LineFollow::config( - messageIn.readByte(), //KP - messageIn.readByte(), //KD - messageIn.readByte(), //robotSpeed - messageIn.readByte() //IntegrationTime - ); - break; - } - } - //delay(5); -} -uint8_t RobotMotorBoard::parseCodename(uint8_t codename){ - switch(codename){ - case B_TK1: - return TK1; - case B_TK2: - return TK2; - case B_TK3: - return TK3; - case B_TK4: - return TK4; - } -} -uint8_t RobotMotorBoard::codenameToAPin(uint8_t codename){ - switch(codename){ - case B_TK1: - return A0; - case B_TK2: - return A1; - case B_TK3: - return A6; - case B_TK4: - return A11; - } -} - -void RobotMotorBoard::setMode(uint8_t mode){ - if(mode==MODE_LINE_FOLLOW){ - LineFollow::calibIRs(); - } - /*if(mode==SET_MOTOR_ADJUSTMENT){ - save_motor_adjustment_to_EEPROM(); - } - */ - /*if(mode==MODE_IR_CONTROL){ - beginIRReceiver(); - }*/ - this->mode=mode; - //stopCurrentActions();//If line following, this should stop the motors -} - -void RobotMotorBoard::stopCurrentActions(){ - motorsStop(); - //motorsWrite(0,0); -} - -void RobotMotorBoard::motorsWrite(int speedL, int speedR){ - /*Serial.print(speedL); - Serial.print(" "); - Serial.println(speedR);*/ - //motor adjustment, using percentage - _refreshMotorAdjustment(); - - if(motorAdjustment<0){ - speedR*=(1+motorAdjustment); - }else{ - speedL*=(1-motorAdjustment); - } - - if(speedR>0){ - analogWrite(IN_A1,speedR); - analogWrite(IN_A2,0); - }else{ - analogWrite(IN_A1,0); - analogWrite(IN_A2,-speedR); - } - - if(speedL>0){ - analogWrite(IN_B1,speedL); - analogWrite(IN_B2,0); - }else{ - analogWrite(IN_B1,0); - analogWrite(IN_B2,-speedL); - } -} -void RobotMotorBoard::motorsWritePct(int speedLpct, int speedRpct){ - //speedLpct, speedRpct ranges from -100 to 100 - motorsWrite(speedLpct*2.55,speedRpct*2.55); -} -void RobotMotorBoard::motorsStop(){ - analogWrite(IN_A1,255); - analogWrite(IN_A2,255); - - analogWrite(IN_B1,255); - analogWrite(IN_B2,255); -} - - -/* -* -* -* Input and Output ports -* -* -*/ -void RobotMotorBoard::_digitalWrite(uint8_t codename,bool value){ - uint8_t pin=parseCodename(codename); - digitalWrite(pin,value); -} -void RobotMotorBoard::_analogWrite(uint8_t codename,int value){ - //There's no PWM available on motor board -} -void RobotMotorBoard::_digitalRead(uint8_t codename){ - uint8_t pin=parseCodename(codename); - bool value=digitalRead(pin); - messageOut.writeByte(COMMAND_DIGITAL_READ_RE); - messageOut.writeByte(codename); - messageOut.writeByte(value); - messageOut.sendData(); -} -void RobotMotorBoard::_analogRead(uint8_t codename){ - uint8_t pin=codenameToAPin(codename); - int value=analogRead(pin); - messageOut.writeByte(COMMAND_ANALOG_READ_RE); - messageOut.writeByte(codename); - messageOut.writeInt(value); - messageOut.sendData(); -} -int RobotMotorBoard::IRread(uint8_t num){ - return _IRread(num-1); //To make consistant with the pins labeled on the board -} - -int RobotMotorBoard::_IRread(uint8_t num){ - IRs.selectPin(num); - return IRs.getAnalogValue(); -} - - -void RobotMotorBoard::_readIR(){ - int value; - messageOut.writeByte(COMMAND_READ_IR_RE); - for(int i=0;i<5;i++){ - value=_IRread(i); - messageOut.writeInt(value); - } - messageOut.sendData(); -} - -void RobotMotorBoard::_readTrim(){ - int value=analogRead(TRIM); - messageOut.writeByte(COMMAND_READ_TRIM_RE); - messageOut.writeInt(value); - messageOut.sendData(); -} - -void RobotMotorBoard::_refreshMotorAdjustment(){ - motorAdjustment=map(analogRead(TRIM),0,1023,-30,30)/100.0; -} - -void RobotMotorBoard::reportActionDone(){ - setMode(MODE_SIMPLE); - messageOut.writeByte(COMMAND_ACTION_DONE); - messageOut.sendData(); -} - -RobotMotorBoard RobotMotor=RobotMotorBoard(); diff --git a/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.h b/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.h deleted file mode 100644 index 1d3a713..0000000 --- a/avr/libraries/LottieLemon/src/utility/ArduinoRobotMotorBoard.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - Copyright (c) 2012 Arduino LLC. All right 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. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef ArduinoRobot_h -#define ArduinoRobot_h - -#include "EasyTransfer2.h" -#include "Multiplexer.h" -#include "LineFollow.h" -//#include "IRremote.h" - -#if ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -//Command code -#define COMMAND_SWITCH_MODE 0 -#define COMMAND_RUN 10 -#define COMMAND_MOTORS_STOP 11 -#define COMMAND_ANALOG_WRITE 20 -#define COMMAND_DIGITAL_WRITE 30 -#define COMMAND_ANALOG_READ 40 -#define COMMAND_ANALOG_READ_RE 41 -#define COMMAND_DIGITAL_READ 50 -#define COMMAND_DIGITAL_READ_RE 51 -#define COMMAND_READ_IR 60 -#define COMMAND_READ_IR_RE 61 -#define COMMAND_ACTION_DONE 70 -#define COMMAND_READ_TRIM 80 -#define COMMAND_READ_TRIM_RE 81 -#define COMMAND_PAUSE_MODE 90 -#define COMMAND_LINE_FOLLOW_CONFIG 100 - - -//component codename -#define CN_LEFT_MOTOR 0 -#define CN_RIGHT_MOTOR 1 -#define CN_IR 2 - -//motor board modes -#define MODE_SIMPLE 0 -#define MODE_LINE_FOLLOW 1 -#define MODE_ADJUST_MOTOR 2 -#define MODE_IR_CONTROL 3 - -//bottom TKs, just for communication purpose -#define B_TK1 201 -#define B_TK2 202 -#define B_TK3 203 -#define B_TK4 204 - -/* -A message structure will be: -switch mode (2): - byte COMMAND_SWITCH_MODE, byte mode -run (5): - byte COMMAND_RUN, int speedL, int speedR -analogWrite (3): - byte COMMAND_ANALOG_WRITE, byte codename, byte value; -digitalWrite (3): - byte COMMAND_DIGITAL_WRITE, byte codename, byte value; -analogRead (2): - byte COMMAND_ANALOG_READ, byte codename; -analogRead _return_ (4): - byte COMMAND_ANALOG_READ_RE, byte codename, int value; -digitalRead (2): - byte COMMAND_DIGITAL_READ, byte codename; -digitalRead _return_ (4): - byte COMMAND_DIGITAL_READ_RE, byte codename, int value; -read IR (1): - byte COMMAND_READ_IR; -read IR _return_ (9): - byte COMMAND_READ_IR_RE, int valueA, int valueB, int valueC, int valueD; - - -*/ - -class RobotMotorBoard:public LineFollow{ - public: - RobotMotorBoard(); - void begin(); - - void process(); - - void parseCommand(); - - int IRread(uint8_t num); - - void setMode(uint8_t mode); - void pauseMode(bool onOff); - - void motorsWrite(int speedL, int speedR); - void motorsWritePct(int speedLpct, int speedRpct);//write motor values in percentage - void motorsStop(); - private: - float motorAdjustment;//-1.0 ~ 1.0, whether left is lowered or right is lowered - - //convert codename to actual pins - uint8_t parseCodename(uint8_t codename); - uint8_t codenameToAPin(uint8_t codename); - - void stopCurrentActions(); - //void sendCommand(byte command,byte codename,int value); - - void _analogWrite(uint8_t codename, int value); - void _digitalWrite(uint8_t codename, bool value); - void _analogRead(uint8_t codename); - void _digitalRead(uint8_t codename); - int _IRread(uint8_t num); - void _readIR(); - void _readTrim(); - - void _refreshMotorAdjustment(); - - Multiplexer IRs; - uint8_t mode; - uint8_t isPaused; - EasyTransfer2 messageIn; - EasyTransfer2 messageOut; - - //Line Following - void reportActionDone(); -}; - -extern RobotMotorBoard RobotMotor; - -#endif diff --git a/avr/libraries/LottieLemon/src/utility/EasyTransfer2.cpp b/avr/libraries/LottieLemon/src/utility/EasyTransfer2.cpp deleted file mode 100644 index bf17678..0000000 --- a/avr/libraries/LottieLemon/src/utility/EasyTransfer2.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/****************************************************************** -* EasyTransfer Arduino Library -* details and example sketch: -* http://www.billporter.info/easytransfer-arduino-library/ -* -* Brought to you by: -* Bill Porter -* www.billporter.info -* -* See Readme for other info and version history -* -* -*This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ - -#include "EasyTransfer2.h" - - - - -//Captures address and size of struct -void EasyTransfer2::begin(HardwareSerial *theSerial){ - _serial = theSerial; - - //dynamic creation of rx parsing buffer in RAM - //rx_buffer = (uint8_t*) malloc(size); - - resetData(); -} - -void EasyTransfer2::writeByte(uint8_t dat){ - if(position<20) - data[position++]=dat; - size++; -} -void EasyTransfer2::writeInt(int dat){ - if(position<19){ - data[position++]=dat>>8; - data[position++]=dat; - size+=2; - } -} -uint8_t EasyTransfer2::readByte(){ - if(position>=size)return 0; - return data[position++]; -} -int EasyTransfer2::readInt(){ - if(position+1>=size)return 0; - int dat_1=data[position++]<<8; - int dat_2=data[position++]; - int dat= dat_1 | dat_2; - return dat; -} - -void EasyTransfer2::resetData(){ - for(int i=0;i<20;i++){ - data[i]=0; - } - size=0; - position=0; -} - -//Sends out struct in binary, with header, length info and checksum -void EasyTransfer2::sendData(){ - uint8_t CS = size; - _serial->write(0x06); - _serial->write(0x85); - _serial->write(size); - for(int i = 0; iwrite(*(data+i)); - //Serial.print(*(data+i)); - //Serial.print(","); - } - //Serial.println(""); - _serial->write(CS); - - resetData(); -} - -boolean EasyTransfer2::receiveData(){ - - //start off by looking for the header bytes. If they were already found in a previous call, skip it. - if(rx_len == 0){ - //this size check may be redundant due to the size check below, but for now I'll leave it the way it is. - if(_serial->available() >= 3){ - //this will block until a 0x06 is found or buffer size becomes less then 3. - while(_serial->read() != 0x06) { - //This will trash any preamble junk in the serial buffer - //but we need to make sure there is enough in the buffer to process while we trash the rest - //if the buffer becomes too empty, we will escape and try again on the next call - if(_serial->available() < 3) - return false; - } - //Serial.println("head"); - if (_serial->read() == 0x85){ - rx_len = _serial->read(); - //Serial.print("rx_len:"); - //Serial.println(rx_len); - resetData(); - - //make sure the binary structs on both Arduinos are the same size. - /*if(rx_len != size){ - rx_len = 0; - return false; - }*/ - } - } - //Serial.println("nothing"); - } - - //we get here if we already found the header bytes, the struct size matched what we know, and now we are byte aligned. - if(rx_len != 0){ - - while(_serial->available() && rx_array_inx <= rx_len){ - data[rx_array_inx++] = _serial->read(); - } - - if(rx_len == (rx_array_inx-1)){ - //seem to have got whole message - //last uint8_t is CS - calc_CS = rx_len; - //Serial.print("len:"); - //Serial.println(rx_len); - for (int i = 0; i -* -*This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. -*To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or -*send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. -******************************************************************/ -#ifndef EasyTransfer2_h -#define EasyTransfer2_h - - -//make it a little prettier on the front end. -#define details(name) (byte*)&name,sizeof(name) - -//Not neccessary, but just in case. -#if ARDUINO > 22 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif -#include "HardwareSerial.h" -//#include -#include -#include -#include -#include - -class EasyTransfer2 { -public: -void begin(HardwareSerial *theSerial); -//void begin(uint8_t *, uint8_t, NewSoftSerial *theSerial); -void sendData(); -boolean receiveData(); - -void writeByte(uint8_t dat); -void writeInt(int dat); -uint8_t readByte(); -int readInt(); - - -private: -HardwareSerial *_serial; - -void resetData(); - -uint8_t data[20]; //data storage, for both read and send -uint8_t position; -uint8_t size; //size of data in bytes. Both for read and send -//uint8_t * address; //address of struct -//uint8_t size; //size of struct -//uint8_t * rx_buffer; //address for temporary storage and parsing buffer -//uint8_t rx_buffer[20]; -uint8_t rx_array_inx; //index for RX parsing buffer -uint8_t rx_len; //RX packet length according to the packet -uint8_t calc_CS; //calculated Chacksum -}; - - - -#endif \ No newline at end of file diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp new file mode 100644 index 0000000..2db826c --- /dev/null +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp @@ -0,0 +1,170 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "LottieLemonLineFollow.h" +using namespace LottieLemon; + +//#define KP 19 //0.1 units +//#define KD 14 +//#define ROBOT_SPEED 100 //percentage + +//#define KP 11 +//#define KD 5 +//#define ROBOT_SPEED 50 + +//#define INTEGRATION_TIME 10 //En ms + +/*uint8_t KP=11; +uint8_t KD=5; +uint8_t robotSpeed=50; //percentage +uint8_t intergrationTime=10;*/ + +#define NIVEL_PARA_LINEA 50 + +/*int lectura_sensor[5], last_error=0, acu=0; + +//Estos son los arrays que hay que rellenar con los valores de los sensores +//de suelo sobre blanco y negro. +int sensor_blanco[]={ +0,0,0,0,0}; +int sensor_negro[]={ +1023,1023,1023,1023,1023}; +*/ +//unsigned long time; + +//void mueve_robot(int vel_izq, int vel_der); +//void para_robot(); +//void doCalibration(int speedPct, int time); +//void ajusta_niveles(); //calibrate values + +LineFollow::LineFollow() { + /*KP=11; + KD=5; + robotSpeed=50; //percentage + intergrationTime=10;*/ + config(11, 5, 50, 10); + + for (int i = 0; i<5; i++) { + sensor_blanco[i] = 0; + sensor_negro[i] = 1023; + } +} + +void LineFollow::config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime) { + this->KP = KP; + this->KD = KD; + this->robotSpeed = robotSpeed; + this->intergrationTime = intergrationTime; + /*Serial.print("LFC: "); + Serial.print(KP); + Serial.print(' '); + Serial.print(KD); + Serial.print(' '); + Serial.print(robotSpeed); + Serial.print(' '); + Serial.println(intergrationTime);*/ + +} +void LineFollow::calibIRs() { + static bool isInited = false;//So only init once + if (isInited)return; + + delay(1000); + + doCalibration(30, 500); + doCalibration(-30, 800); + doCalibration(30, 500); + + delay(1000); + isInited = true; +} + +void LineFollow::runLineFollow() { + for (int count = 0; count<5; count++) + { + lectura_sensor[count] = map(_IRread(count), sensor_negro[count], sensor_blanco[count], 0, 127); + acu += lectura_sensor[count]; + } + + //Serial.println(millis()); + if (acu > NIVEL_PARA_LINEA) + { + acu /= 5; + + int error = ((lectura_sensor[0] << 6) + (lectura_sensor[1] << 5) - (lectura_sensor[3] << 5) - (lectura_sensor[4] << 6)) / acu; + + error = constrain(error, -100, 100); + + //Calculamos la correcion de velocidad mediante un filtro PD + int vel = (error * KP) / 10 + (error - last_error)*KD; + + last_error = error; + + //Corregimos la velocidad de avance con el error de salida del filtro PD + int motor_left = constrain((robotSpeed + vel), -100, 100); + int motor_right = constrain((robotSpeed - vel), -100, 100); + + //Movemos el robot + //motorsWritePct(motor_left,motor_right); + motorsWritePct(motor_left, motor_right); + + //Esperamos un poquito a que el robot reaccione + delay(intergrationTime); + } + else + { + //Hemos encontrado una linea negra + //perpendicular a nuestro camino + //paramos el robot + motorsStop(); + + //y detenemos la ejecucion del programa + //while(true); + reportActionDone(); + //setMode(MODE_SIMPLE); + } +} + + +void LineFollow::doCalibration(int speedPct, unsigned int time) { + motorsWritePct(speedPct, -speedPct); + unsigned long beginTime = millis(); + while ((millis() - beginTime) sensor_blanco[count]) + sensor_blanco[count] = lectura; + + if (lectura < sensor_negro[count]) + sensor_negro[count] = lectura; + } +} + + + + + diff --git a/avr/libraries/LottieLemon/src/utility/LineFollow.h b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.h similarity index 75% rename from avr/libraries/LottieLemon/src/utility/LineFollow.h rename to avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.h index 0ae0e4c..7dce073 100644 --- a/avr/libraries/LottieLemon/src/utility/LineFollow.h +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.h @@ -16,43 +16,42 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef LINE_FOLLOW_H -#define LINE_FOLLOW_H +#ifndef LOTTIE_LEMON_LINE_FOLLOW_H +#define LOTTIE_LEMON_LINE_FOLLOW_H -#if ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif +#include -class LineFollow{ +namespace LottieLemon { + + class LineFollow { public: LineFollow(); void calibIRs(); void runLineFollow(); void config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime); - + //These are all pure virtual functions, pure VF needs pure specifier "=0" //virtual void motorsWrite(int speedL, int speedR)=0; - virtual void motorsWritePct(int speedLpct, int speedRpct)=0; - virtual void motorsStop()=0; - virtual int _IRread(uint8_t num)=0; + virtual void motorsWritePct(int speedLpct, int speedRpct) = 0; + virtual void motorsStop() = 0; + virtual int _IRread(uint8_t num) = 0; protected: - virtual void reportActionDone()=0; - + virtual void reportActionDone() = 0; + private: - void doCalibration(int speedPct, int time); + void doCalibration(int speedPct, unsigned int time); void ajusta_niveles(); - + uint8_t KP; uint8_t KD; uint8_t robotSpeed; //percentage uint8_t intergrationTime; - + int lectura_sensor[5], last_error, acu; int sensor_blanco[5]; int sensor_negro[5]; -}; + }; +} -#endif +#endif // LOTTIE_LEMON_LINE_FOLLOW_H diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp new file mode 100644 index 0000000..83559d2 --- /dev/null +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp @@ -0,0 +1,309 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../LottieLemon.h" + +using namespace LottieLemon; + +static MotorBoard * _motorBoard = nullptr; + +MotorBoard::MotorBoard() : _multiplexer{ MUXI, MUXA, MUXB, MUXC } { + _motorBoard = this; + TwoWayIntegerEasyTransfer.attach( + [](uint8_t command, IntegerEasyTransfer & request) { + if (_motorBoard != nullptr) { + if (!_motorBoard->handleMessage(command, request)) { + // TODO: Report unhandled command. + } + } + }); +} +/*void RobotMotorBoard::beginIRReceiver(){ +IRrecv::enableIRIn(); +}*/ +void MotorBoard::begin() { + //init MUX + _multiplexer.pinMode(MUX_IN, INPUT); + _multiplexer.enable(); + + isPaused = false; +} + +void MotorBoard::run() { + if (isPaused)return;//skip process if the mode is paused + + if (mode == MODE_SIMPLE) { + //Serial.println("s"); + //do nothing? Simple mode is just about getting commands + } + else if (mode == MODE_LINE_FOLLOW) { + //do line following stuff here. + LineFollow::runLineFollow(); + } + else if (mode == MODE_ADJUST_MOTOR) { + //Serial.println('a'); + //motorAdjustment=analogRead(POT); + //setSpeed(255,255); + //delay(100); + } +} +void MotorBoard::pauseMode(bool onOff) { + if (onOff) { + isPaused = true; + } + else { + isPaused = false; + } + stopCurrentActions(); + +} +bool MotorBoard::handleMessage(uint8_t command, IntegerEasyTransfer & request) { + uint8_t modeName; + uint8_t codename; + int value; + int speedL; + int speedR; + //Serial.println("data received"); + //Serial.println(command); + switch (command) { + case COMMAND_SWITCH_MODE: + modeName = request.readByte(); + setMode(modeName); + break; + case COMMAND_RUN: + if (mode == MODE_LINE_FOLLOW) + break;//in follow line mode, the motor does not follow commands + speedL = request.readInt(); + speedR = request.readInt(); + motorsWrite(speedL, speedR); + break; + case COMMAND_MOTORS_STOP: + motorsStop(); + break; + case COMMAND_ANALOG_WRITE: + codename = request.readByte(); + value = request.readInt(); + _analogWrite(codename, value); + break; + case COMMAND_DIGITAL_WRITE: + codename = request.readByte(); + value = request.readByte(); + _digitalWrite(codename, value); + break; + case COMMAND_ANALOG_READ: + codename = request.readByte(); + _analogRead(codename); + break; + case COMMAND_DIGITAL_READ: + codename = request.readByte(); + _digitalRead(codename); + break; + case COMMAND_READ_IR: + _readIR(); + break; + case COMMAND_READ_TRIM: + _readTrim(); + break; + case COMMAND_PAUSE_MODE: + pauseMode(request.readByte());//onOff state + break; + case COMMAND_LINE_FOLLOW_CONFIG: + LineFollow::config( + request.readByte(), //KP + request.readByte(), //KD + request.readByte(), //robotSpeed + request.readByte() //IntegrationTime + ); + break; + default: + return false; + } + return true; +} +uint8_t MotorBoard::parseCodename(uint8_t codename) { + switch (codename) { + case B_TK1: + return TK1; + case B_TK2: + return TK2; + case B_TK3: + return TK3; + case B_TK4: + return TK4; + default: + return 0; + } +} +uint8_t MotorBoard::codenameToAPin(uint8_t codename) { + switch (codename) { + case B_TK1: + return A0; + case B_TK2: + return A1; + case B_TK3: + return A6; + case B_TK4: + return A11; + default: + return 0; + } +} + +void MotorBoard::setMode(uint8_t mode) { + if (mode == MODE_LINE_FOLLOW) { + LineFollow::calibIRs(); + } + /*if(mode==SET_MOTOR_ADJUSTMENT){ + save_motor_adjustment_to_EEPROM(); + } + */ + /*if(mode==MODE_IR_CONTROL){ + beginIRReceiver(); + }*/ + this->mode = mode; + //stopCurrentActions();//If line following, this should stop the motors +} + +void MotorBoard::stopCurrentActions() { + motorsStop(); + //motorsWrite(0,0); +} + +void MotorBoard::motorsWrite(int speedL, int speedR) { + /*Serial.print(speedL); + Serial.print(" "); + Serial.println(speedR);*/ + //motor adjustment, using percentage + _refreshMotorAdjustment(); + + if (motorAdjustment<0) { + speedR *= (1 + motorAdjustment); + } + else { + speedL *= (1 - motorAdjustment); + } + + if (speedR>0) { + analogWrite(IN_A1, speedR); + analogWrite(IN_A2, 0); + } + else { + analogWrite(IN_A1, 0); + analogWrite(IN_A2, -speedR); + } + + if (speedL>0) { + analogWrite(IN_B1, speedL); + analogWrite(IN_B2, 0); + } + else { + analogWrite(IN_B1, 0); + analogWrite(IN_B2, -speedL); + } +} +void MotorBoard::motorsWritePct(int speedLpct, int speedRpct) { + //speedLpct, speedRpct ranges from -100 to 100 + motorsWrite(speedLpct*2.55, speedRpct*2.55); +} +void MotorBoard::motorsStop() { + analogWrite(IN_A1, 255); + analogWrite(IN_A2, 255); + + analogWrite(IN_B1, 255); + analogWrite(IN_B2, 255); +} + + +/* + * + * + * Input and Output ports + * + * + */ +void MotorBoard::_digitalWrite(uint8_t codename, bool value) { + uint8_t pin = parseCodename(codename); + if (pin == 0) { return; } // Not valid codename. + digitalWrite(pin, value); +} +void MotorBoard::_analogWrite(uint8_t, int) { + //There's no PWM available on motor board +} +void MotorBoard::_digitalRead(uint8_t codename) { + uint8_t pin = parseCodename(codename); + if (pin == 0) { return; } // Not valid codename. + bool value = digitalRead(pin); + TwoWayIntegerEasyTransfer.writeByte(COMMAND_DIGITAL_READ_RE); + TwoWayIntegerEasyTransfer.writeByte(codename); + TwoWayIntegerEasyTransfer.writeByte(value); + TwoWayIntegerEasyTransfer.sendData(); +} +void MotorBoard::_analogRead(uint8_t codename) { + uint8_t pin = codenameToAPin(codename); + if (pin == 0) { return; } // Not valid codename. + int value = analogRead(pin); + TwoWayIntegerEasyTransfer.writeByte(COMMAND_ANALOG_READ_RE); + TwoWayIntegerEasyTransfer.writeByte(codename); + TwoWayIntegerEasyTransfer.writeInt(value); + TwoWayIntegerEasyTransfer.sendData(); +} +int MotorBoard::IRread(uint8_t num) { + return _IRread(num - 1); //To make consistant with the pins labeled on the board +} + +int MotorBoard::_IRread(uint8_t num) { + if (num < 5) { + return _multiplexer.analogRead(num); + } + return 0; +} + + +void MotorBoard::_readIR() { + int value; + TwoWayIntegerEasyTransfer.writeByte(COMMAND_READ_IR_RE); + for (int i = 0; i<5; i++) { + value = _IRread(i); + TwoWayIntegerEasyTransfer.writeInt(value); + } + TwoWayIntegerEasyTransfer.sendData(); +} + +void MotorBoard::_readTrim() { + int value = analogRead(TRIM); + TwoWayIntegerEasyTransfer.writeByte(COMMAND_READ_TRIM_RE); + TwoWayIntegerEasyTransfer.writeInt(value); + TwoWayIntegerEasyTransfer.sendData(); +} + +void MotorBoard::_refreshMotorAdjustment() { + motorAdjustment = map(analogRead(TRIM), 0, 1023, -30, 30) / 100.0; +} + +void MotorBoard::reportActionDone() { + setMode(MODE_SIMPLE); + TwoWayIntegerEasyTransfer.writeByte(COMMAND_ACTION_DONE); + TwoWayIntegerEasyTransfer.sendData(); +} + +void MotorBoard::reset() { + begin(); + stopCurrentActions(); +} diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h new file mode 100644 index 0000000..46d0449 --- /dev/null +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef LOTTIE_LEMON_MOTOR_BOARD_H +#define LOTTIE_LEMON_MOTOR_BOARD_H + +#include +#include +#include "LottieLemonLineFollow.h" +//#include "IRremote.h" + +namespace LottieLemon { + + class MotorBoard : public LineFollow, public IntegerEasyTransferFeature { + public: + MotorBoard(); + void begin(); + + void run(); + + int IRread(uint8_t num); + + void setMode(uint8_t mode); + void pauseMode(bool onOff); + + void motorsWrite(int speedL, int speedR); + void motorsWritePct(int speedLpct, int speedRpct);//write motor values in percentage + void motorsStop(); + + bool handleMessage(uint8_t command, IntegerEasyTransfer & request); + void reset(); + + private: + float motorAdjustment;//-1.0 ~ 1.0, whether left is lowered or right is lowered + + //convert codename to actual pins + uint8_t parseCodename(uint8_t codename); + uint8_t codenameToAPin(uint8_t codename); + + void stopCurrentActions(); + //void sendCommand(byte command,byte codename,int value); + + void _analogWrite(uint8_t codename, int value); + void _digitalWrite(uint8_t codename, bool value); + void _analogRead(uint8_t codename); + void _digitalRead(uint8_t codename); + int _IRread(uint8_t num); + void _readIR(); + void _readTrim(); + + void _refreshMotorAdjustment(); + + AMxx4051 _multiplexer; + uint8_t mode; + uint8_t isPaused; + + //Line Following + void reportActionDone(); + }; +} + +#endif // LOTTIE_LEMON_MOTOR_BOARD_H diff --git a/avr/libraries/LottieLemon/src/utility/Multiplexer.cpp b/avr/libraries/LottieLemon/src/utility/Multiplexer.cpp deleted file mode 100644 index 71a6d05..0000000 --- a/avr/libraries/LottieLemon/src/utility/Multiplexer.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright (c) 2012 Arduino LLC. All right 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. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "Multiplexer.h" - -void Multiplexer::begin(uint8_t* selectors, uint8_t Z, uint8_t length){ - for(uint8_t i=0;iselectors[i]=selectors[i]; - pinMode(selectors[i],OUTPUT); - } - this->length=length; - this->pin_Z=Z; - pinMode(pin_Z,INPUT); -} - -void Multiplexer::selectPin(uint8_t num){ - for(uint8_t i=0;i= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -class Multiplexer{ - public: - void begin(uint8_t* selectors, uint8_t Z, uint8_t length); - void selectPin(uint8_t num); - int getAnalogValue(); - int getAnalogValueAt(uint8_t num); - bool getDigitalValue(); - bool getDigitalValueAt(uint8_t num); - private: - uint8_t selectors[4]; - uint8_t pin_Z; - uint8_t length; -}; - -#endif diff --git a/avr/libraries/LottieLemon/src/utility/lineFollow.cpp b/avr/libraries/LottieLemon/src/utility/lineFollow.cpp deleted file mode 100644 index 08964e9..0000000 --- a/avr/libraries/LottieLemon/src/utility/lineFollow.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - Copyright (c) 2012 Arduino LLC. All right 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. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -//#include -#include "LineFollow.h" - -//#define KP 19 //0.1 units -//#define KD 14 -//#define ROBOT_SPEED 100 //percentage - -//#define KP 11 -//#define KD 5 -//#define ROBOT_SPEED 50 - -//#define INTEGRATION_TIME 10 //En ms - -/*uint8_t KP=11; -uint8_t KD=5; -uint8_t robotSpeed=50; //percentage -uint8_t intergrationTime=10;*/ - -#define NIVEL_PARA_LINEA 50 - -/*int lectura_sensor[5], last_error=0, acu=0; - -//Estos son los arrays que hay que rellenar con los valores de los sensores -//de suelo sobre blanco y negro. -int sensor_blanco[]={ - 0,0,0,0,0}; -int sensor_negro[]={ - 1023,1023,1023,1023,1023}; -*/ -//unsigned long time; - -//void mueve_robot(int vel_izq, int vel_der); -//void para_robot(); -//void doCalibration(int speedPct, int time); -//void ajusta_niveles(); //calibrate values - -LineFollow::LineFollow(){ - /*KP=11; - KD=5; - robotSpeed=50; //percentage - intergrationTime=10;*/ - config(11,5,50,10); - - for(int i=0;i<5;i++){ - sensor_blanco[i]=0; - sensor_negro[i]=1023; - } -} - -void LineFollow::config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime){ - this->KP=KP; - this->KD=KD; - this->robotSpeed=robotSpeed; - this->intergrationTime=intergrationTime; - /*Serial.print("LFC: "); - Serial.print(KP); - Serial.print(' '); - Serial.print(KD); - Serial.print(' '); - Serial.print(robotSpeed); - Serial.print(' '); - Serial.println(intergrationTime);*/ - -} -void LineFollow::calibIRs(){ - static bool isInited=false;//So only init once - if(isInited)return ; - - delay(1000); - - doCalibration(30,500); - doCalibration(-30,800); - doCalibration(30,500); - - delay(1000); - isInited=true; -} - -void LineFollow::runLineFollow(){ - for(int count=0; count<5; count++) - { - lectura_sensor[count]=map(_IRread(count),sensor_negro[count],sensor_blanco[count],0,127); - acu+=lectura_sensor[count]; - } - - //Serial.println(millis()); - if (acu > NIVEL_PARA_LINEA) - { - acu/=5; - - int error = ((lectura_sensor[0]<<6)+(lectura_sensor[1]<<5)-(lectura_sensor[3]<<5)-(lectura_sensor[4]<<6))/acu; - - error = constrain(error,-100,100); - - //Calculamos la correcion de velocidad mediante un filtro PD - int vel = (error * KP)/10 + (error-last_error)*KD; - - last_error = error; - - //Corregimos la velocidad de avance con el error de salida del filtro PD - int motor_left = constrain((robotSpeed + vel),-100,100); - int motor_right =constrain((robotSpeed - vel),-100,100); - - //Movemos el robot - //motorsWritePct(motor_left,motor_right); - motorsWritePct(motor_left,motor_right); - - //Esperamos un poquito a que el robot reaccione - delay(intergrationTime); - } - else - { - //Hemos encontrado una linea negra - //perpendicular a nuestro camino - //paramos el robot - motorsStop(); - - //y detenemos la ejecución del programa - //while(true); - reportActionDone(); - //setMode(MODE_SIMPLE); - } -} - - -void LineFollow::doCalibration(int speedPct, int time){ - motorsWritePct(speedPct, -speedPct); - unsigned long beginTime = millis(); - while((millis()-beginTime) sensor_blanco[count]) - sensor_blanco[count]=lectura; - - if (lectura < sensor_negro[count]) - sensor_negro[count]=lectura; - } -} - - - - - - From b513c9d13b6020408d883d41f02a0e71ea9d5064 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sun, 5 Mar 2017 16:51:50 +0100 Subject: [PATCH 50/67] Added capability to measure the arduino robot battery --- .../src/utility/LottieLemonMotorBoard.cpp | 21 +++++++++++++++++++ .../src/utility/LottieLemonMotorBoard.h | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp index 83559d2..f0985f7 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp @@ -307,3 +307,24 @@ void MotorBoard::reset() { begin(); stopCurrentActions(); } + +float MotorBoard::getBatteryTerminalVolts() { + // Read battery voltage on multiplexer channel 5. + int adcValue = _multiplexer.analogRead(5); + float adcVoltage = (5.0f / 1023.0f) * adcValue; + return 2.0f * adcVoltage; // Compensate for voltage divider. +} + +float MotorBoard::getBatteryChargeMilliamps() { + // Read battery charge current on multiplexer channel 6. + int adcValue = _multiplexer.analogRead(6); + float adcVoltage = (5.0f / 1023.0f) * adcValue; + return adcVoltage / 0.00075f; // Compensate sense resistor and gain. +} + +float MotorBoard::getBatteryDischargeMilliamps() { + // Read battery discharge current on multiplexer channel 7. + int adcValue = _multiplexer.analogRead(7); + float adcVoltage = (5.0f / 1023.0f) * adcValue; + return adcVoltage / 0.00075f; // Compensate sense resistor and gain. +} diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h index 46d0449..468ac10 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h @@ -35,6 +35,10 @@ namespace LottieLemon { int IRread(uint8_t num); + float getBatteryTerminalVolts(); + float getBatteryChargeMilliamps(); + float getBatteryDischargeMilliamps(); + void setMode(uint8_t mode); void pauseMode(bool onOff); From d2db7f9d1d332e77b39e4e68679bfac93b0865a4 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sun, 5 Mar 2017 17:12:13 +0100 Subject: [PATCH 51/67] Added example sketch to readout the battery stats --- .../Robot_Motor_Battery_Test.ino | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 avr/libraries/LottieLemon/examples/Robot_Motor_Battery_Test/Robot_Motor_Battery_Test.ino diff --git a/avr/libraries/LottieLemon/examples/Robot_Motor_Battery_Test/Robot_Motor_Battery_Test.ino b/avr/libraries/LottieLemon/examples/Robot_Motor_Battery_Test/Robot_Motor_Battery_Test.ino new file mode 100644 index 0000000..39610e6 --- /dev/null +++ b/avr/libraries/LottieLemon/examples/Robot_Motor_Battery_Test/Robot_Motor_Battery_Test.ino @@ -0,0 +1,59 @@ +/* Motor Core with battery stats readout. + + This code for the Arduino Robot's motor board + is the stock firmware with added code printing + the battery status. + +*/ + +#include +#include +#include + +enum { + SW_RX = SDA, + SW_TX = SCL +}; + +SoftwareSerial swSerial{ SW_RX, SW_TX }; + +LottieLemon::MotorBoard motorBoard; + +void setup() { + // start serial communication + swSerial.begin(19200); + // initialize the libraries + Serial1.begin(9600); + TwoWayIntegerEasyTransfer.begin(&Serial1); + TwoWayIntegerEasyTransfer.attach([]() { doSystemReset(); }); + doSystemReset(); +} + +void loop() { + if (TwoWayIntegerEasyTransfer.hasReceivedData()) { + TwoWayIntegerEasyTransfer.processInput(); + } + motorBoard.run(); + + measureBattery(); +} + +void doSystemReset() { + motorBoard.reset(); +} + +void measureBattery() { + static String bar; // string for storing the information + static unsigned long tStart = millis(); + unsigned long tStop = millis(); + if ((tStop - tStart) > 100) { + bar = String(""); // empty the string + // read the sensors and add them to the string + bar = bar + "Vbat=" + motorBoard.getBatteryTerminalVolts() + 'V' + + "\tIcharge=" + motorBoard.getBatteryChargeMilliamps() + "mA" + + "\tIdischarge=" + motorBoard.getBatteryDischargeMilliamps() + "mA"; + + swSerial.println(bar); + tStart = tStop; + } +} From c9c6900452ee1e0dfa5fb44177c318db7a364772 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sun, 5 Mar 2017 21:34:17 +0100 Subject: [PATCH 52/67] Added IntegerEasyTransfer and LottieLemon libraries --- avr/platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/platform.txt b/avr/platform.txt index 0f32823..d2d3291 100644 --- a/avr/platform.txt +++ b/avr/platform.txt @@ -5,7 +5,7 @@ # - https://github.com/arduino/Arduino/wiki/Arduino-Hardware-Cores-migration-guide-from-1.0-to-1.6 name=Arduino Robot Boards -version=1.0.2 +version=1.0.3 # AVR compile variables # --------------------- From 95300d406a5d007d03eeefb5290f7ea30a1f3644 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Wed, 8 Mar 2017 20:30:03 +0100 Subject: [PATCH 53/67] NOT_A_PIN is now allowed for the enable pin of a AnalogMultiplexer that has been tied to GND --- avr/libraries/AnalogMultiplexer/README.md | 2 +- .../AnalogMultiplexerExample.ino | 2 +- avr/libraries/AnalogMultiplexer/library.properties | 2 +- .../AnalogMultiplexer/src/AnalogMultiplexer.cpp | 12 +++++++++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/avr/libraries/AnalogMultiplexer/README.md b/avr/libraries/AnalogMultiplexer/README.md index 576482c..c4e4cbc 100644 --- a/avr/libraries/AnalogMultiplexer/README.md +++ b/avr/libraries/AnalogMultiplexer/README.md @@ -7,7 +7,7 @@ A library to interface with analog multiplexers. Supports 4051, 4052, 4053, and #include enum { - PIN_EN = 2, // The enable pin of the multiplexer. + PIN_EN = 2, // The enable pin of the multiplexer or NOT_A_PIN. PIN_S0 = 3, // Channel selector pin 0. PIN_S1 = 4, // Channel selector pin 1. PIN_S2 = 5, // Channel selector pin 2. diff --git a/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino b/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino index bb24b26..3973cfb 100644 --- a/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino +++ b/avr/libraries/AnalogMultiplexer/examples/AnalogMultiplexerExample/AnalogMultiplexerExample.ino @@ -2,7 +2,7 @@ // Selected Arduino pins. enum { - PIN_EN = 2, // The enable pin of the multiplexer. + PIN_EN = 2, // The enable pin of the multiplexer or NOT_A_PIN. PIN_S0 = 3, // Channel selector pin 0. PIN_S1 = 4, // Channel selector pin 1. PIN_S2 = 5, // Channel selector pin 2. diff --git a/avr/libraries/AnalogMultiplexer/library.properties b/avr/libraries/AnalogMultiplexer/library.properties index 227b950..4e30139 100644 --- a/avr/libraries/AnalogMultiplexer/library.properties +++ b/avr/libraries/AnalogMultiplexer/library.properties @@ -1,5 +1,5 @@ name=AnalogMultiplexer -version=1.0.0 +version=1.0.1 author=Julian Sanin maintainer=Julian Sanin sentence=A library to interface with analog multiplexers. diff --git a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp index 5a61f13..780d73e 100644 --- a/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp +++ b/avr/libraries/AnalogMultiplexer/src/AnalogMultiplexer.cpp @@ -29,15 +29,21 @@ AnalogMultiplexer::AnalogMultiplexer(uint8_t pinEnable) { _pinEnable = pinEnable; _pinCommon = NOT_A_PIN; - /*Arduino*/::pinMode(_pinEnable, OUTPUT); + if (NOT_A_PIN != _pinEnable) { + /*Arduino*/::pinMode(_pinEnable, OUTPUT); + } } void AnalogMultiplexer::enable() { - /*Arduino*/::digitalWrite(_pinEnable, LOW); + if (NOT_A_PIN != _pinEnable) { + /*Arduino*/::digitalWrite(_pinEnable, LOW); + } } void AnalogMultiplexer::disable() { - /*Arduino*/::digitalWrite(_pinEnable, HIGH); + if (NOT_A_PIN != _pinEnable) { + /*Arduino*/::digitalWrite(_pinEnable, HIGH); + } } void AnalogMultiplexer::pinMode(uint8_t pinCommon, uint8_t mode) { From 42bd006d66bf79201dcc4de631fbf893216aa1b0 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Wed, 8 Mar 2017 21:02:25 +0100 Subject: [PATCH 54/67] Added some functionality of ControlBoard and usage of board macros for compilation --- avr/libraries/LottieLemon/src/LottieLemon.h | 5 + .../src/utility/LottieLemonControlBoard.cpp | 174 ++++++++++++++++++ .../src/utility/LottieLemonControlBoard.h | 56 ++++++ .../src/utility/LottieLemonMotorBoard.cpp | 4 + 4 files changed, 239 insertions(+) create mode 100644 avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp create mode 100644 avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h diff --git a/avr/libraries/LottieLemon/src/LottieLemon.h b/avr/libraries/LottieLemon/src/LottieLemon.h index b627d5f..252871b 100644 --- a/avr/libraries/LottieLemon/src/LottieLemon.h +++ b/avr/libraries/LottieLemon/src/LottieLemon.h @@ -19,7 +19,12 @@ #ifndef LOTTIE_LEMON_H #define LOTTIE_LEMON_H +#ifdef ARDUINO_AVR_ROBOT_MOTOR #include "utility/LottieLemonMotorBoard.h" +#endif +#ifdef ARDUINO_AVR_ROBOT_CONTROL +#include "utility/LottieLemonControlBoard.h" +#endif namespace LottieLemon { diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp new file mode 100644 index 0000000..f3c7cac --- /dev/null +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp @@ -0,0 +1,174 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../LottieLemon.h" + +#ifdef ARDUINO_AVR_ROBOT_CONTROL + +using namespace LottieLemon; + +static void _requestData(uint8_t command) { + TwoWayIntegerEasyTransfer.writeByte(command); + TwoWayIntegerEasyTransfer.sendData(); + if (TwoWayIntegerEasyTransfer.hasReceivedData()) { + TwoWayIntegerEasyTransfer.processInput(); + } +} + +ControlBoard::ControlBoard() + : _multiplexer{ NOT_A_PIN, MUXA, MUXB, MUXC, MUXD } { +} + +void ControlBoard::setMode(uint8_t mode) { + TwoWayIntegerEasyTransfer.writeByte(COMMAND_SWITCH_MODE); + TwoWayIntegerEasyTransfer.writeByte(mode); + TwoWayIntegerEasyTransfer.sendData(); +} + +void ControlBoard::pauseMode(bool isPaused) { + TwoWayIntegerEasyTransfer.writeByte(COMMAND_PAUSE_MODE); + TwoWayIntegerEasyTransfer.writeByte(isPaused); + TwoWayIntegerEasyTransfer.sendData(); +} + +bool ControlBoard::isActionDone(void) { + static bool actionValue; + TwoWayIntegerEasyTransfer.attach( + [](uint8_t command, IntegerEasyTransfer &) { + if (command == COMMAND_ACTION_DONE) { + actionValue = true; + } + else { + actionValue = false; + } + }); + if (TwoWayIntegerEasyTransfer.hasReceivedData()) { + TwoWayIntegerEasyTransfer.processInput(); + } + return actionValue; +} + +void ControlBoard::lineFollowConfig( + uint8_t kP, uint8_t kD, + uint8_t robotSpeedPercentage, uint8_t intergrationTimeMillis) { + TwoWayIntegerEasyTransfer.writeByte(COMMAND_LINE_FOLLOW_CONFIG); + TwoWayIntegerEasyTransfer.writeByte(kP); + TwoWayIntegerEasyTransfer.writeByte(kD); + TwoWayIntegerEasyTransfer.writeByte(robotSpeedPercentage); + TwoWayIntegerEasyTransfer.writeByte(intergrationTimeMillis); + TwoWayIntegerEasyTransfer.sendData(); +} + +void ControlBoard::motorsWrite(int speedLeft, int speedRight) { + TwoWayIntegerEasyTransfer.writeByte(COMMAND_RUN); + TwoWayIntegerEasyTransfer.writeInt(speedLeft); + TwoWayIntegerEasyTransfer.writeInt(speedRight); + TwoWayIntegerEasyTransfer.sendData(); +} + +void ControlBoard::motorsStop(void) { + TwoWayIntegerEasyTransfer.writeByte(COMMAND_MOTORS_STOP); + TwoWayIntegerEasyTransfer.sendData(); +} + +bool ControlBoard::digitalRead(uint8_t port) { + return false; +} + +void ControlBoard::digitalWrite(uint8_t port, bool value) { + /* + //bottom TKs, just for communication purpose + enum { + B_TK1 = 201, + B_TK2 = 202, + B_TK3 = 203, + B_TK4 = 204 + }; + #define D10 B_TK1 + #define D9 B_TK2 + #define D8 B_TK4 + #define D7 B_TK3 + */ + /* + Type Code Label + D12 [TKD5] (D5) MUXC Not safe to use! + D6 [TKD4] (D4) MUXA Not safe to use! + A4 [TKD3] (D3) + A3 [TKD2] (D2) + A2 [TKD1] (D1) + A1 [TKD0] (D0) + D17 [LED1] (LED) + Type Code Label + A11 [B_TK4] (D8) + A6 [B_TK3] (D7) + A1 [B_TK2] (D9) + A0 [B_TK1] (D10) + X0..X7 @ mux + */ +} + +int ControlBoard::analogRead(uint8_t port) { + return 0; +} + +void ControlBoard::analogWrite(uint8_t port, uint8_t value) { +} + +uint8_t ControlBoard::updateIR(uint16_t * ir, uint8_t size) { + static const uint8_t MAX_IR_DATA = 5; + static uint16_t irValues[MAX_IR_DATA]; + static uint8_t maxItems; + maxItems = min(MAX_IR_DATA, size); + TwoWayIntegerEasyTransfer.attach( + [](uint8_t command, IntegerEasyTransfer & request) { + for (uint16_t i = 0; i < MAX_IR_DATA; i++) { + if (command == COMMAND_READ_IR_RE) { + irValues[i] = request.readInt(); + } + else { + irValues[i] = 0; + } + } + }); + _requestData(COMMAND_READ_IR); + memcpy(ir, irValues, maxItems * sizeof(uint16_t)); + return maxItems; +} + +int LottieLemon::ControlBoard::trimRead() { + static int trimmerValue; + TwoWayIntegerEasyTransfer.attach( + [](uint8_t command, IntegerEasyTransfer & request) { + if (command == COMMAND_READ_TRIM_RE) { + trimmerValue = request.readInt(); + } + else { + trimmerValue = 0; + } + }); + _requestData(COMMAND_READ_TRIM); + return trimmerValue; +} + +int ControlBoard::knobRead() { + return /*Arduino*/::analogRead(POT); +} + +#endif // ARDUINO_AVR_ROBOT_CONTROL diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h new file mode 100644 index 0000000..dc9118a --- /dev/null +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2012 Arduino LLC. All right 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. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef LOTTIE_LEMON_CONTROL_BOARD_H +#define LOTTIE_LEMON_CONTROL_BOARD_H + +#include +#include + +namespace LottieLemon { + + class ControlBoard { + + AMxx4067 _multiplexer; + + public: + ControlBoard(); + void setMode(uint8_t mode); + void pauseMode(bool isPaused); + bool isActionDone(void); + void lineFollowConfig( + uint8_t kP, uint8_t kD, + uint8_t robotSpeedPercentage, uint8_t intergrationTimeMillis + ); + + void motorsWrite(int speedLeft, int speedRight); + void motorsStop(void); + + bool digitalRead(uint8_t port /*TK0..TK7, TKD0..TKD5, B_TK1..B_TK4*/); + void digitalWrite(uint8_t port /*TKD0..TKD5, B_TK1..B_TK4*/, bool value); + int analogRead(uint8_t port /*TK0..TK7, TKD0..TKD5, B_TK1..B_TK4*/); + void analogWrite(uint8_t port /*TKD4*/, uint8_t value); + + uint8_t updateIR(uint16_t * /*[out]*/ ir, uint8_t size); + int trimRead(void); + + int knobRead(void); + }; +} + +#endif // LOTTIE_LEMON_CONTROL_BOARD_H diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp index f0985f7..452de86 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp @@ -20,6 +20,8 @@ #include #include "../LottieLemon.h" +#ifdef ARDUINO_AVR_ROBOT_MOTOR + using namespace LottieLemon; static MotorBoard * _motorBoard = nullptr; @@ -328,3 +330,5 @@ float MotorBoard::getBatteryDischargeMilliamps() { float adcVoltage = (5.0f / 1023.0f) * adcValue; return adcVoltage / 0.00075f; // Compensate sense resistor and gain. } + +#endif // ARDUINO_AVR_ROBOT_MOTOR From b1b0e1d7391fc7aa64e3ad2902c1f39f5f98eadf Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 11 Mar 2017 07:51:05 +0100 Subject: [PATCH 55/67] Fixed the bug when calling LineFollow::config(uint8_t,uint8_t,uint8_t,uint8_t) where data was read in reverse order. In C++ the order of evaluation of function arguments is unspecified. --- .../src/utility/LottieLemonMotorBoard.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp index 452de86..7f2243f 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp @@ -82,6 +82,10 @@ bool MotorBoard::handleMessage(uint8_t command, IntegerEasyTransfer & request) { int value; int speedL; int speedR; + uint8_t kP; + uint8_t kD; + uint8_t robotSpeedPct; + uint8_t integrationTimeMillis; //Serial.println("data received"); //Serial.println(command); switch (command) { @@ -127,12 +131,11 @@ bool MotorBoard::handleMessage(uint8_t command, IntegerEasyTransfer & request) { pauseMode(request.readByte());//onOff state break; case COMMAND_LINE_FOLLOW_CONFIG: - LineFollow::config( - request.readByte(), //KP - request.readByte(), //KD - request.readByte(), //robotSpeed - request.readByte() //IntegrationTime - ); + kP = request.readByte(); + kD = request.readByte(); + robotSpeedPct = request.readByte(); + integrationTimeMillis = request.readByte(); + LineFollow::config(kP, kD, robotSpeedPct, integrationTimeMillis); break; default: return false; From 7d954929dcb6b4ed278a77dbcabfe394e76d7c80 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 11 Mar 2017 16:50:21 +0100 Subject: [PATCH 56/67] Added new PID algorithm --- .../src/utility/LottieLemonLineFollow.cpp | 166 +++++------------- .../src/utility/LottieLemonLineFollow.h | 32 ++-- 2 files changed, 64 insertions(+), 134 deletions(-) diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp index 2db826c..bbabe7c 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.cpp @@ -20,151 +20,75 @@ #include "LottieLemonLineFollow.h" using namespace LottieLemon; -//#define KP 19 //0.1 units -//#define KD 14 -//#define ROBOT_SPEED 100 //percentage - -//#define KP 11 -//#define KD 5 -//#define ROBOT_SPEED 50 - -//#define INTEGRATION_TIME 10 //En ms - -/*uint8_t KP=11; -uint8_t KD=5; -uint8_t robotSpeed=50; //percentage -uint8_t intergrationTime=10;*/ - -#define NIVEL_PARA_LINEA 50 - -/*int lectura_sensor[5], last_error=0, acu=0; - -//Estos son los arrays que hay que rellenar con los valores de los sensores -//de suelo sobre blanco y negro. -int sensor_blanco[]={ -0,0,0,0,0}; -int sensor_negro[]={ -1023,1023,1023,1023,1023}; -*/ -//unsigned long time; - -//void mueve_robot(int vel_izq, int vel_der); -//void para_robot(); -//void doCalibration(int speedPct, int time); -//void ajusta_niveles(); //calibrate values - LineFollow::LineFollow() { - /*KP=11; - KD=5; - robotSpeed=50; //percentage - intergrationTime=10;*/ config(11, 5, 50, 10); - - for (int i = 0; i<5; i++) { - sensor_blanco[i] = 0; - sensor_negro[i] = 1023; - } } -void LineFollow::config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime) { - this->KP = KP; - this->KD = KD; - this->robotSpeed = robotSpeed; - this->intergrationTime = intergrationTime; - /*Serial.print("LFC: "); - Serial.print(KP); - Serial.print(' '); - Serial.print(KD); - Serial.print(' '); - Serial.print(robotSpeed); - Serial.print(' '); - Serial.println(intergrationTime);*/ +void LineFollow::config(uint8_t KP, uint8_t KD, + uint8_t robotSpeedPct, uint8_t integrationTimeMillis) { + config(KP, 0, KD, robotSpeedPct, integrationTimeMillis); +} +void LineFollow::config(uint8_t KP, uint8_t KI, uint8_t KD, + uint8_t robotSpeedPct, uint8_t integrationTimeMillis) { + _KP = KP; + _KI = KI; + _KD = KD; + _robotSpeedPct = robotSpeedPct; + _integrationTimeMillis = integrationTimeMillis; } -void LineFollow::calibIRs() { - static bool isInited = false;//So only init once - if (isInited)return; +void LineFollow::calibIRs() { + // So only init once. + static bool isInited = false; + if (isInited) { + return; + } delay(1000); - doCalibration(30, 500); doCalibration(-30, 800); doCalibration(30, 500); - delay(1000); isInited = true; } void LineFollow::runLineFollow() { - for (int count = 0; count<5; count++) - { - lectura_sensor[count] = map(_IRread(count), sensor_negro[count], sensor_blanco[count], 0, 127); - acu += lectura_sensor[count]; - } - - //Serial.println(millis()); - if (acu > NIVEL_PARA_LINEA) - { - acu /= 5; - - int error = ((lectura_sensor[0] << 6) + (lectura_sensor[1] << 5) - (lectura_sensor[3] << 5) - (lectura_sensor[4] << 6)) / acu; - - error = constrain(error, -100, 100); - - //Calculamos la correcion de velocidad mediante un filtro PD - int vel = (error * KP) / 10 + (error - last_error)*KD; - - last_error = error; - - //Corregimos la velocidad de avance con el error de salida del filtro PD - int motor_left = constrain((robotSpeed + vel), -100, 100); - int motor_right = constrain((robotSpeed - vel), -100, 100); - - //Movemos el robot - //motorsWritePct(motor_left,motor_right); - motorsWritePct(motor_left, motor_right); - - //Esperamos un poquito a que el robot reaccione - delay(intergrationTime); + enum { STOP_LINE_VALUE = 1500 }; + uint32_t weight = 0; + uint16_t sum = 0; + for (int i = 0; i < 5; i++) { + _sensors[i] = _IRread(i); + weight += _sensors[i] * (i + 1); + sum += _sensors[i]; } - else - { - //Hemos encontrado una linea negra - //perpendicular a nuestro camino - //paramos el robot + if (sum > STOP_LINE_VALUE) { + if ((millis() - _tStartMillis) <= _integrationTimeMillis) { + return; + } + _tStartMillis = millis(); + int16_t error = 100 * weight / sum - 300; + _integral = _integral + error; + _derivative = error - _lastError; + _lastError = error; + // Calculate the adjustment with PID control. + int16_t change = _KP * error + _KI * _integral + _KD * _derivative; + change = change / 10; + // Change motor speed. + int16_t leftSpeed = constrain((_robotSpeedPct + change), -100, 100); + int16_t rightSpeed = constrain((_robotSpeedPct - change), -100, 100); + motorsWritePct(leftSpeed, rightSpeed); + } else { + // The robot discovered a perpendicular black line. Stop motors. motorsStop(); - - //y detenemos la ejecucion del programa - //while(true); + // Stop the execution of the control board program. reportActionDone(); - //setMode(MODE_SIMPLE); } } - void LineFollow::doCalibration(int speedPct, unsigned int time) { motorsWritePct(speedPct, -speedPct); unsigned long beginTime = millis(); - while ((millis() - beginTime) sensor_blanco[count]) - sensor_blanco[count] = lectura; - - if (lectura < sensor_negro[count]) - sensor_negro[count] = lectura; - } -} - - - - - diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.h b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.h index 7dce073..5485e5c 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.h +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonLineFollow.h @@ -27,30 +27,36 @@ namespace LottieLemon { public: LineFollow(); - void calibIRs(); void runLineFollow(); - void config(uint8_t KP, uint8_t KD, uint8_t robotSpeed, uint8_t intergrationTime); - + void config(uint8_t KP, uint8_t KD, + uint8_t robotSpeedPct, uint8_t integrationTimeMillis + ); + void config(uint8_t KP, uint8_t KI, uint8_t KD, + uint8_t robotSpeedPct, uint8_t integrationTimeMillis + ); //These are all pure virtual functions, pure VF needs pure specifier "=0" //virtual void motorsWrite(int speedL, int speedR)=0; virtual void motorsWritePct(int speedLpct, int speedRpct) = 0; virtual void motorsStop() = 0; - virtual int _IRread(uint8_t num) = 0; protected: + virtual int _IRread(uint8_t num) = 0; virtual void reportActionDone() = 0; + void calibIRs(); private: void doCalibration(int speedPct, unsigned int time); - void ajusta_niveles(); - - uint8_t KP; - uint8_t KD; - uint8_t robotSpeed; //percentage - uint8_t intergrationTime; - int lectura_sensor[5], last_error, acu; - int sensor_blanco[5]; - int sensor_negro[5]; + uint8_t _KP; + uint8_t _KI; + uint8_t _KD; + uint8_t _robotSpeedPct; + uint8_t _integrationTimeMillis; + unsigned long _tStartMillis; + + uint16_t _sensors[5]; + int16_t _integral = 0; + int16_t _derivative = 0; + int16_t _lastError = 0; }; } From 5e171e271a83d1322d899f741db28ba440ede3ad Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sun, 12 Mar 2017 20:53:35 +0100 Subject: [PATCH 57/67] Implemented read and write methods but pinMode is still missing! --- avr/libraries/LottieLemon/src/LottieLemon.h | 124 ++++++++---- .../src/utility/LottieLemonControlBoard.cpp | 191 ++++++++++-------- .../src/utility/LottieLemonControlBoard.h | 13 +- 3 files changed, 202 insertions(+), 126 deletions(-) diff --git a/avr/libraries/LottieLemon/src/LottieLemon.h b/avr/libraries/LottieLemon/src/LottieLemon.h index 252871b..93453d3 100644 --- a/avr/libraries/LottieLemon/src/LottieLemon.h +++ b/avr/libraries/LottieLemon/src/LottieLemon.h @@ -19,17 +19,38 @@ #ifndef LOTTIE_LEMON_H #define LOTTIE_LEMON_H -#ifdef ARDUINO_AVR_ROBOT_MOTOR -#include "utility/LottieLemonMotorBoard.h" -#endif -#ifdef ARDUINO_AVR_ROBOT_CONTROL -#include "utility/LottieLemonControlBoard.h" -#endif +#include namespace LottieLemon { + /* + A message structure will be: + switch mode (2): + byte COMMAND_SWITCH_MODE, byte mode + run (5): + byte COMMAND_RUN, int speedL, int speedR + analogWrite (3): + byte COMMAND_ANALOG_WRITE, byte codename, byte value; + digitalWrite (3): + byte COMMAND_DIGITAL_WRITE, byte codename, byte value; + analogRead (2): + byte COMMAND_ANALOG_READ, byte codename; + analogRead _return_ (4): + byte COMMAND_ANALOG_READ_RE, byte codename, int value; + digitalRead (2): + byte COMMAND_DIGITAL_READ, byte codename; + digitalRead _return_ (4): + byte COMMAND_DIGITAL_READ_RE, byte codename, int value; + read IR (1): + byte COMMAND_READ_IR; + read IR _return_ (9): + byte COMMAND_READ_IR_RE, int valueA, int valueB, int valueC, int valueD; + + + */ + //Command code - enum { + enum Command { COMMAND_SWITCH_MODE = 0, COMMAND_RUN = 10, COMMAND_MOTORS_STOP = 11, @@ -48,55 +69,72 @@ namespace LottieLemon { COMMAND_LINE_FOLLOW_CONFIG = 100 }; - - //component codename - enum { - CN_LEFT_MOTOR = 0, - CN_RIGHT_MOTOR = 1, - CN_IR = 2 - }; - //motor board modes - enum { + enum Mode { MODE_SIMPLE = 0, MODE_LINE_FOLLOW = 1, MODE_ADJUST_MOTOR = 2, MODE_IR_CONTROL = 3 }; - //bottom TKs, just for communication purpose - enum { + //bottom TKs, just for communication purpose, DR, DW, AR + enum BottomMicrocontrollerPin { // keep and extend to silkscreen names B_TK1 = 201, B_TK2 = 202, B_TK3 = 203, - B_TK4 = 204 + B_TK4 = 204, + BOT_D7 = B_TK3, + BOT_D8 = B_TK4, + BOT_D9 = B_TK2, + BOT_D10 = B_TK1 }; - /* - A message structure will be: - switch mode (2): - byte COMMAND_SWITCH_MODE, byte mode - run (5): - byte COMMAND_RUN, int speedL, int speedR - analogWrite (3): - byte COMMAND_ANALOG_WRITE, byte codename, byte value; - digitalWrite (3): - byte COMMAND_DIGITAL_WRITE, byte codename, byte value; - analogRead (2): - byte COMMAND_ANALOG_READ, byte codename; - analogRead _return_ (4): - byte COMMAND_ANALOG_READ_RE, byte codename, int value; - digitalRead (2): - byte COMMAND_DIGITAL_READ, byte codename; - digitalRead _return_ (4): - byte COMMAND_DIGITAL_READ_RE, byte codename, int value; - read IR (1): - byte COMMAND_READ_IR; - read IR _return_ (9): - byte COMMAND_READ_IR_RE, int valueA, int valueB, int valueC, int valueD; - +#ifdef ARDUINO_AVR_ROBOT_CONTROL + //top TKs + enum TopMultiplexerPin { + T_TK0 = 100, + T_TK1 = 101, + T_TK2 = 102, + T_TK3 = 103, + T_TK4 = 104, + T_TK5 = 105, + T_TK6 = 106, + T_TK7 = 107, + TOP_M0 = T_TK0, + TOP_M1 = T_TK1, + TOP_M2 = T_TK2, + TOP_M3 = T_TK3, + TOP_M4 = T_TK4, + TOP_M5 = T_TK5, + TOP_M6 = T_TK6, + TOP_M7 = T_TK7 + }; - */ + //top TKDs + enum TopMicrocontrollerPin { + T_TKD0 = TKD0, + T_TKD1 = TKD1, + T_TKD2 = TKD2, + T_TKD3 = TKD3, + T_TKD4 = TKD4, // Shared with MUXA + T_TKD5 = TKD5, // Shared with MUXC + T_LED1 = LED1, + TOP_D0 = T_TKD0, + TOP_D1 = T_TKD1, + TOP_D2 = T_TKD2, + TOP_D3 = T_TKD3, + TOP_D4 = T_TKD4, // Shared with MUXA + TOP_D5 = T_TKD5, // Shared with MUXC + TOP_LED1 = T_LED1 + }; +#endif // ARDUINO_AVR_ROBOT_CONTROL } +#ifdef ARDUINO_AVR_ROBOT_MOTOR +#include "utility/LottieLemonMotorBoard.h" +#endif +#ifdef ARDUINO_AVR_ROBOT_CONTROL +#include "utility/LottieLemonControlBoard.h" +#endif + #endif // LOTTIE_LEMON_H diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp index f3c7cac..b03bd89 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp @@ -24,111 +24,148 @@ using namespace LottieLemon; -static void _requestData(uint8_t command) { - TwoWayIntegerEasyTransfer.writeByte(command); - TwoWayIntegerEasyTransfer.sendData(); +static void processRequest() { if (TwoWayIntegerEasyTransfer.hasReceivedData()) { TwoWayIntegerEasyTransfer.processInput(); } } +template +static void sendData(const Tail & tail) { + TwoWayIntegerEasyTransfer.writeByte(tail); + TwoWayIntegerEasyTransfer.sendData(); +} + +template +static void requestData(const Tail & tail) { + sendData(tail); + processRequest(); +} + +template +static void sendData(const Head & head, const Body & ... body) { + TwoWayIntegerEasyTransfer.writeByte(head); + sendData(body...); +} + +template +static void requestData(const Head & head, const Body & ... body) { + TwoWayIntegerEasyTransfer.writeByte(head); + requestData(body...); +} + ControlBoard::ControlBoard() : _multiplexer{ NOT_A_PIN, MUXA, MUXB, MUXC, MUXD } { } void ControlBoard::setMode(uint8_t mode) { - TwoWayIntegerEasyTransfer.writeByte(COMMAND_SWITCH_MODE); - TwoWayIntegerEasyTransfer.writeByte(mode); - TwoWayIntegerEasyTransfer.sendData(); + sendData(COMMAND_SWITCH_MODE, mode); } void ControlBoard::pauseMode(bool isPaused) { - TwoWayIntegerEasyTransfer.writeByte(COMMAND_PAUSE_MODE); - TwoWayIntegerEasyTransfer.writeByte(isPaused); - TwoWayIntegerEasyTransfer.sendData(); + sendData(COMMAND_PAUSE_MODE, isPaused); } bool ControlBoard::isActionDone(void) { static bool actionValue; TwoWayIntegerEasyTransfer.attach( [](uint8_t command, IntegerEasyTransfer &) { - if (command == COMMAND_ACTION_DONE) { + if (command == COMMAND_ACTION_DONE) actionValue = true; - } - else { + else actionValue = false; - } }); - if (TwoWayIntegerEasyTransfer.hasReceivedData()) { - TwoWayIntegerEasyTransfer.processInput(); - } + processRequest(); return actionValue; } void ControlBoard::lineFollowConfig( uint8_t kP, uint8_t kD, uint8_t robotSpeedPercentage, uint8_t intergrationTimeMillis) { - TwoWayIntegerEasyTransfer.writeByte(COMMAND_LINE_FOLLOW_CONFIG); - TwoWayIntegerEasyTransfer.writeByte(kP); - TwoWayIntegerEasyTransfer.writeByte(kD); - TwoWayIntegerEasyTransfer.writeByte(robotSpeedPercentage); - TwoWayIntegerEasyTransfer.writeByte(intergrationTimeMillis); - TwoWayIntegerEasyTransfer.sendData(); + sendData(COMMAND_LINE_FOLLOW_CONFIG, + kP, kD, robotSpeedPercentage, intergrationTimeMillis); } void ControlBoard::motorsWrite(int speedLeft, int speedRight) { - TwoWayIntegerEasyTransfer.writeByte(COMMAND_RUN); - TwoWayIntegerEasyTransfer.writeInt(speedLeft); - TwoWayIntegerEasyTransfer.writeInt(speedRight); - TwoWayIntegerEasyTransfer.sendData(); + sendData(COMMAND_RUN, speedLeft, speedRight); } void ControlBoard::motorsStop(void) { - TwoWayIntegerEasyTransfer.writeByte(COMMAND_MOTORS_STOP); - TwoWayIntegerEasyTransfer.sendData(); + sendData(COMMAND_MOTORS_STOP); +} + +bool ControlBoard::digitalRead(TopMicrocontrollerPin pin) { + // TODO: set pinMode + return /*Arduino*/::digitalRead(pin); } -bool ControlBoard::digitalRead(uint8_t port) { - return false; -} - -void ControlBoard::digitalWrite(uint8_t port, bool value) { - /* - //bottom TKs, just for communication purpose - enum { - B_TK1 = 201, - B_TK2 = 202, - B_TK3 = 203, - B_TK4 = 204 - }; - #define D10 B_TK1 - #define D9 B_TK2 - #define D8 B_TK4 - #define D7 B_TK3 - */ - /* - Type Code Label - D12 [TKD5] (D5) MUXC Not safe to use! - D6 [TKD4] (D4) MUXA Not safe to use! - A4 [TKD3] (D3) - A3 [TKD2] (D2) - A2 [TKD1] (D1) - A1 [TKD0] (D0) - D17 [LED1] (LED) - Type Code Label - A11 [B_TK4] (D8) - A6 [B_TK3] (D7) - A1 [B_TK2] (D9) - A0 [B_TK1] (D10) - X0..X7 @ mux - */ -} - -int ControlBoard::analogRead(uint8_t port) { - return 0; -} - -void ControlBoard::analogWrite(uint8_t port, uint8_t value) { +bool ControlBoard::digitalRead(TopMultiplexerPin pin) { + int channel = pin - T_TK0; + // TODO: set pinMode + _multiplexer.pinMode(MUX_IN, INPUT); + return _multiplexer.digitalRead(channel); +} + +bool ControlBoard::digitalRead(BottomMicrocontrollerPin pin) { + // TODO: is pinMode set? + static uint8_t pinCode; + static uint8_t pinValue; + pinCode = pin; + TwoWayIntegerEasyTransfer.attach( + [](uint8_t command, IntegerEasyTransfer & request) { + if ((command == COMMAND_DIGITAL_READ_RE) && + (request.readByte() == pinCode)) + pinValue = request.readByte(); + else + pinValue = 0; + }); + requestData(COMMAND_DIGITAL_READ, pinCode); + return (pinValue != 0); +} + +void ControlBoard::digitalWrite(TopMicrocontrollerPin pin, bool value) { + // TODO: set pinMode + /*Arduino*/::digitalWrite(pin, value); +} + +void ControlBoard::digitalWrite(BottomMicrocontrollerPin pin, bool value) { + // TODO: is pinMode set? + sendData(COMMAND_DIGITAL_WRITE, pin, value); +} + +int ControlBoard::analogRead(TopMicrocontrollerPin pin) { + // TODO: set pinMode + return /*Arduino*/::analogRead(pin); +} + +int ControlBoard::analogRead(TopMultiplexerPin pin) { + int channel = pin - T_TK0; + // TODO: set pinMode + _multiplexer.pinMode(MUX_IN, INPUT); + return _multiplexer.analogRead(channel); +} + +int ControlBoard::analogRead(BottomMicrocontrollerPin pin) { + // TODO: is pinMode set? + static uint8_t pinCode = pin; + static int pinValue; + pinCode = pin; + TwoWayIntegerEasyTransfer.attach( + [](uint8_t command, IntegerEasyTransfer & request) { + if ((command == COMMAND_ANALOG_READ_RE) && + (request.readByte() == pinCode)) + pinValue = request.readInt(); + else + pinValue = 0; + }); + requestData(COMMAND_ANALOG_READ, pinCode); + return pinValue; +} + +void ControlBoard::analogWrite(TopMicrocontrollerPin pin, uint8_t value) { + // TODO: set pinMode + if (pin == T_TKD4) + /*Arduino*/::analogWrite(pin, value); } uint8_t ControlBoard::updateIR(uint16_t * ir, uint8_t size) { @@ -139,15 +176,13 @@ uint8_t ControlBoard::updateIR(uint16_t * ir, uint8_t size) { TwoWayIntegerEasyTransfer.attach( [](uint8_t command, IntegerEasyTransfer & request) { for (uint16_t i = 0; i < MAX_IR_DATA; i++) { - if (command == COMMAND_READ_IR_RE) { + if (command == COMMAND_READ_IR_RE) irValues[i] = request.readInt(); - } - else { + else irValues[i] = 0; - } } }); - _requestData(COMMAND_READ_IR); + requestData(COMMAND_READ_IR); memcpy(ir, irValues, maxItems * sizeof(uint16_t)); return maxItems; } @@ -156,14 +191,12 @@ int LottieLemon::ControlBoard::trimRead() { static int trimmerValue; TwoWayIntegerEasyTransfer.attach( [](uint8_t command, IntegerEasyTransfer & request) { - if (command == COMMAND_READ_TRIM_RE) { + if (command == COMMAND_READ_TRIM_RE) trimmerValue = request.readInt(); - } - else { + else trimmerValue = 0; - } }); - _requestData(COMMAND_READ_TRIM); + requestData(COMMAND_READ_TRIM); return trimmerValue; } diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h index dc9118a..a04b40f 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h @@ -41,10 +41,15 @@ namespace LottieLemon { void motorsWrite(int speedLeft, int speedRight); void motorsStop(void); - bool digitalRead(uint8_t port /*TK0..TK7, TKD0..TKD5, B_TK1..B_TK4*/); - void digitalWrite(uint8_t port /*TKD0..TKD5, B_TK1..B_TK4*/, bool value); - int analogRead(uint8_t port /*TK0..TK7, TKD0..TKD5, B_TK1..B_TK4*/); - void analogWrite(uint8_t port /*TKD4*/, uint8_t value); + bool digitalRead(TopMicrocontrollerPin pin); + bool digitalRead(TopMultiplexerPin pin); + bool digitalRead(BottomMicrocontrollerPin pin); + void digitalWrite(TopMicrocontrollerPin pin, bool value); + void digitalWrite(BottomMicrocontrollerPin pin, bool value); + int analogRead(TopMicrocontrollerPin pin); + int analogRead(TopMultiplexerPin pin); + int analogRead(BottomMicrocontrollerPin pin); + void analogWrite(TopMicrocontrollerPin pin, uint8_t value); uint8_t updateIR(uint16_t * /*[out]*/ ir, uint8_t size); int trimRead(void); From 4d327e3cc3e224e7da672079e3779fa99069f644 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Mon, 13 Mar 2017 12:26:39 +0100 Subject: [PATCH 58/67] Added new command for pinMode to communication protocol --- avr/libraries/LottieLemon/src/LottieLemon.h | 7 ++++--- .../LottieLemon/src/utility/LottieLemonMotorBoard.cpp | 9 +++++++++ .../LottieLemon/src/utility/LottieLemonMotorBoard.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/avr/libraries/LottieLemon/src/LottieLemon.h b/avr/libraries/LottieLemon/src/LottieLemon.h index 93453d3..10594d0 100644 --- a/avr/libraries/LottieLemon/src/LottieLemon.h +++ b/avr/libraries/LottieLemon/src/LottieLemon.h @@ -66,7 +66,8 @@ namespace LottieLemon { COMMAND_READ_TRIM = 80, COMMAND_READ_TRIM_RE = 81, COMMAND_PAUSE_MODE = 90, - COMMAND_LINE_FOLLOW_CONFIG = 100 + COMMAND_LINE_FOLLOW_CONFIG = 100, + COMMAND_PIN_MODE = 110 }; //motor board modes @@ -77,8 +78,8 @@ namespace LottieLemon { MODE_IR_CONTROL = 3 }; - //bottom TKs, just for communication purpose, DR, DW, AR - enum BottomMicrocontrollerPin { // keep and extend to silkscreen names + //bottom TKs, just for communication purpose + enum BottomMicrocontrollerPin { B_TK1 = 201, B_TK2 = 202, B_TK3 = 203, diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp index 7f2243f..67357f1 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp @@ -103,6 +103,10 @@ bool MotorBoard::handleMessage(uint8_t command, IntegerEasyTransfer & request) { case COMMAND_MOTORS_STOP: motorsStop(); break; + case COMMAND_PIN_MODE: + codename = request.readByte(); + value = request.readInt(); + _pinMode(codename, value); case COMMAND_ANALOG_WRITE: codename = request.readByte(); value = request.readInt(); @@ -243,6 +247,11 @@ void MotorBoard::motorsStop() { * * */ +void MotorBoard::_pinMode(uint8_t codename, int value) { + uint8_t pin = parseCodename(codename); + if (pin == 0) { return; } // Not valid codename. + pinMode(pin, value); +} void MotorBoard::_digitalWrite(uint8_t codename, bool value) { uint8_t pin = parseCodename(codename); if (pin == 0) { return; } // Not valid codename. diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h index 468ac10..0d2ce2f 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h @@ -59,6 +59,7 @@ namespace LottieLemon { void stopCurrentActions(); //void sendCommand(byte command,byte codename,int value); + void _pinMode(uint8_t codename, int value); void _analogWrite(uint8_t codename, int value); void _digitalWrite(uint8_t codename, bool value); void _analogRead(uint8_t codename); From 51a1516f0bc436089623c13134071fd7ae2a2c4a Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Mon, 13 Mar 2017 12:59:34 +0100 Subject: [PATCH 59/67] Added overloaded write method to TwoWayIntegerEasyTransfer class --- avr/libraries/IntegerEasyTransfer/README.txt | 2 ++ avr/libraries/IntegerEasyTransfer/keywords.txt | 1 + .../IntegerEasyTransfer/library.properties | 2 +- .../src/TwoWayIntegerEasyTransfer.cpp | 8 ++++++++ .../src/TwoWayIntegerEasyTransfer.h | 18 ++++++++++++++++++ 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/avr/libraries/IntegerEasyTransfer/README.txt b/avr/libraries/IntegerEasyTransfer/README.txt index 08dbc14..7f2b23b 100644 --- a/avr/libraries/IntegerEasyTransfer/README.txt +++ b/avr/libraries/IntegerEasyTransfer/README.txt @@ -47,6 +47,8 @@ * 1.1.0 * Added new classes to support two way communications and extended * messages and software reset. +* 1.1.1 +* Added overloaded write method to TwoWayIntegerEasyTransfer. * * * Limits of the Library diff --git a/avr/libraries/IntegerEasyTransfer/keywords.txt b/avr/libraries/IntegerEasyTransfer/keywords.txt index 2592c87..19105db 100644 --- a/avr/libraries/IntegerEasyTransfer/keywords.txt +++ b/avr/libraries/IntegerEasyTransfer/keywords.txt @@ -24,6 +24,7 @@ sendData KEYWORD2 receiveData KEYWORD2 writeByte KEYWORD2 writeInt KEYWORD2 +write KEYWORD2 readByte KEYWORD2 readInt KEYWORD2 sendSystemReset KEYWORD2 diff --git a/avr/libraries/IntegerEasyTransfer/library.properties b/avr/libraries/IntegerEasyTransfer/library.properties index 710b79a..6e134d0 100644 --- a/avr/libraries/IntegerEasyTransfer/library.properties +++ b/avr/libraries/IntegerEasyTransfer/library.properties @@ -1,5 +1,5 @@ name=IntegerEasyTransfer -version=1.1.0 +version=1.1.1 author=Bill Porter , Julian Sanin maintainer=Julian Sanin sentence=A library to interface Arduino boards. diff --git a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp index 1c238c7..f2324d0 100644 --- a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp +++ b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.cpp @@ -45,10 +45,18 @@ void CTwoWayIntegerEasyTransfer::writeByte(uint8_t data) { _response.writeByte(data); } +void CTwoWayIntegerEasyTransfer::write(uint8_t data) { + writeByte(data); +} + void CTwoWayIntegerEasyTransfer::writeInt(int16_t data) { _response.writeInt(data); } +void CTwoWayIntegerEasyTransfer::write(int16_t data) { + writeInt(data); +} + bool CTwoWayIntegerEasyTransfer::hasReceivedData(void) { return _request.receiveData(); } diff --git a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h index f7ceac0..545051e 100644 --- a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h +++ b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h @@ -81,6 +81,15 @@ class CTwoWayIntegerEasyTransfer { /// void writeByte(uint8_t data); + /// + /// Adds a byte to the protocol buffer. See also: + /// + /// + /// + /// The byte to be inserted to the buffer. + /// + void write(uint8_t data); + /// /// Adds a integer to the protocol buffer. See also: /// @@ -90,6 +99,15 @@ class CTwoWayIntegerEasyTransfer { /// void writeInt(int16_t data); + /// + /// Adds a integer to the protocol buffer. See also: + /// + /// + /// + /// The integer to be inserted to the buffer. + /// + void write(int16_t data); + /// Check wether messages have been received. /// /// True if a messages has been recieved otherwise false. From e8143cf0228771d66cb127d6bf02d578b2ee0c29 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Mon, 13 Mar 2017 13:44:48 +0100 Subject: [PATCH 60/67] Implemented pinMode --- .../src/utility/LottieLemonControlBoard.cpp | 65 ++++++++++--------- .../src/utility/LottieLemonControlBoard.h | 6 +- .../src/utility/LottieLemonMotorBoard.cpp | 8 +-- .../src/utility/LottieLemonMotorBoard.h | 4 +- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp index b03bd89..323d32d 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.cpp @@ -32,7 +32,7 @@ static void processRequest() { template static void sendData(const Tail & tail) { - TwoWayIntegerEasyTransfer.writeByte(tail); + TwoWayIntegerEasyTransfer.write(tail); TwoWayIntegerEasyTransfer.sendData(); } @@ -44,26 +44,30 @@ static void requestData(const Tail & tail) { template static void sendData(const Head & head, const Body & ... body) { - TwoWayIntegerEasyTransfer.writeByte(head); + TwoWayIntegerEasyTransfer.write(head); sendData(body...); } template static void requestData(const Head & head, const Body & ... body) { - TwoWayIntegerEasyTransfer.writeByte(head); + TwoWayIntegerEasyTransfer.write(head); requestData(body...); } ControlBoard::ControlBoard() : _multiplexer{ NOT_A_PIN, MUXA, MUXB, MUXC, MUXD } { + _multiplexer.pinMode(MUX_IN, INPUT); } void ControlBoard::setMode(uint8_t mode) { - sendData(COMMAND_SWITCH_MODE, mode); + sendData(static_cast(COMMAND_SWITCH_MODE), mode); } void ControlBoard::pauseMode(bool isPaused) { - sendData(COMMAND_PAUSE_MODE, isPaused); + sendData( + static_cast(COMMAND_PAUSE_MODE), + static_cast(isPaused) + ); } bool ControlBoard::isActionDone(void) { @@ -82,35 +86,42 @@ bool ControlBoard::isActionDone(void) { void ControlBoard::lineFollowConfig( uint8_t kP, uint8_t kD, uint8_t robotSpeedPercentage, uint8_t intergrationTimeMillis) { - sendData(COMMAND_LINE_FOLLOW_CONFIG, + sendData(static_cast(COMMAND_LINE_FOLLOW_CONFIG), kP, kD, robotSpeedPercentage, intergrationTimeMillis); } void ControlBoard::motorsWrite(int speedLeft, int speedRight) { - sendData(COMMAND_RUN, speedLeft, speedRight); + sendData(static_cast(COMMAND_RUN), speedLeft, speedRight); } void ControlBoard::motorsStop(void) { - sendData(COMMAND_MOTORS_STOP); + sendData(static_cast(COMMAND_MOTORS_STOP)); +} + +void ControlBoard::pinMode(TopMicrocontrollerPin pin, uint8_t value) { + /*Arduino*/::pinMode(pin, value); +} + +void ControlBoard::pinMode(BottomMicrocontrollerPin pin, uint8_t value) { + sendData( + static_cast(COMMAND_PIN_MODE), + static_cast(pin), value + ); } bool ControlBoard::digitalRead(TopMicrocontrollerPin pin) { - // TODO: set pinMode return /*Arduino*/::digitalRead(pin); } bool ControlBoard::digitalRead(TopMultiplexerPin pin) { int channel = pin - T_TK0; - // TODO: set pinMode - _multiplexer.pinMode(MUX_IN, INPUT); return _multiplexer.digitalRead(channel); } bool ControlBoard::digitalRead(BottomMicrocontrollerPin pin) { - // TODO: is pinMode set? static uint8_t pinCode; static uint8_t pinValue; - pinCode = pin; + pinCode = static_cast(pin); TwoWayIntegerEasyTransfer.attach( [](uint8_t command, IntegerEasyTransfer & request) { if ((command == COMMAND_DIGITAL_READ_RE) && @@ -119,37 +130,34 @@ bool ControlBoard::digitalRead(BottomMicrocontrollerPin pin) { else pinValue = 0; }); - requestData(COMMAND_DIGITAL_READ, pinCode); + requestData(static_cast(COMMAND_DIGITAL_READ), pinCode); return (pinValue != 0); } -void ControlBoard::digitalWrite(TopMicrocontrollerPin pin, bool value) { - // TODO: set pinMode +void ControlBoard::digitalWrite(TopMicrocontrollerPin pin, uint8_t value) { /*Arduino*/::digitalWrite(pin, value); } -void ControlBoard::digitalWrite(BottomMicrocontrollerPin pin, bool value) { - // TODO: is pinMode set? - sendData(COMMAND_DIGITAL_WRITE, pin, value); +void ControlBoard::digitalWrite(BottomMicrocontrollerPin pin, uint8_t value) { + sendData( + static_cast(COMMAND_DIGITAL_WRITE), + static_cast(pin), value + ); } int ControlBoard::analogRead(TopMicrocontrollerPin pin) { - // TODO: set pinMode return /*Arduino*/::analogRead(pin); } int ControlBoard::analogRead(TopMultiplexerPin pin) { int channel = pin - T_TK0; - // TODO: set pinMode - _multiplexer.pinMode(MUX_IN, INPUT); return _multiplexer.analogRead(channel); } int ControlBoard::analogRead(BottomMicrocontrollerPin pin) { - // TODO: is pinMode set? - static uint8_t pinCode = pin; + static uint8_t pinCode; static int pinValue; - pinCode = pin; + pinCode = static_cast(pin); TwoWayIntegerEasyTransfer.attach( [](uint8_t command, IntegerEasyTransfer & request) { if ((command == COMMAND_ANALOG_READ_RE) && @@ -158,12 +166,11 @@ int ControlBoard::analogRead(BottomMicrocontrollerPin pin) { else pinValue = 0; }); - requestData(COMMAND_ANALOG_READ, pinCode); + requestData(static_cast(COMMAND_ANALOG_READ), pinCode); return pinValue; } void ControlBoard::analogWrite(TopMicrocontrollerPin pin, uint8_t value) { - // TODO: set pinMode if (pin == T_TKD4) /*Arduino*/::analogWrite(pin, value); } @@ -182,7 +189,7 @@ uint8_t ControlBoard::updateIR(uint16_t * ir, uint8_t size) { irValues[i] = 0; } }); - requestData(COMMAND_READ_IR); + requestData(static_cast(COMMAND_READ_IR)); memcpy(ir, irValues, maxItems * sizeof(uint16_t)); return maxItems; } @@ -196,7 +203,7 @@ int LottieLemon::ControlBoard::trimRead() { else trimmerValue = 0; }); - requestData(COMMAND_READ_TRIM); + requestData(static_cast(COMMAND_READ_TRIM)); return trimmerValue; } diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h index a04b40f..4864ced 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonControlBoard.h @@ -41,11 +41,13 @@ namespace LottieLemon { void motorsWrite(int speedLeft, int speedRight); void motorsStop(void); + void pinMode(TopMicrocontrollerPin pin, uint8_t value); + void pinMode(BottomMicrocontrollerPin pin, uint8_t value); bool digitalRead(TopMicrocontrollerPin pin); bool digitalRead(TopMultiplexerPin pin); bool digitalRead(BottomMicrocontrollerPin pin); - void digitalWrite(TopMicrocontrollerPin pin, bool value); - void digitalWrite(BottomMicrocontrollerPin pin, bool value); + void digitalWrite(TopMicrocontrollerPin pin, uint8_t value); + void digitalWrite(BottomMicrocontrollerPin pin, uint8_t value); int analogRead(TopMicrocontrollerPin pin); int analogRead(TopMultiplexerPin pin); int analogRead(BottomMicrocontrollerPin pin); diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp index 67357f1..0f3401f 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.cpp @@ -105,8 +105,8 @@ bool MotorBoard::handleMessage(uint8_t command, IntegerEasyTransfer & request) { break; case COMMAND_PIN_MODE: codename = request.readByte(); - value = request.readInt(); - _pinMode(codename, value); + modeName = request.readByte(); + _pinMode(codename, modeName); case COMMAND_ANALOG_WRITE: codename = request.readByte(); value = request.readInt(); @@ -247,12 +247,12 @@ void MotorBoard::motorsStop() { * * */ -void MotorBoard::_pinMode(uint8_t codename, int value) { +void MotorBoard::_pinMode(uint8_t codename, uint8_t value) { uint8_t pin = parseCodename(codename); if (pin == 0) { return; } // Not valid codename. pinMode(pin, value); } -void MotorBoard::_digitalWrite(uint8_t codename, bool value) { +void MotorBoard::_digitalWrite(uint8_t codename, uint8_t value) { uint8_t pin = parseCodename(codename); if (pin == 0) { return; } // Not valid codename. digitalWrite(pin, value); diff --git a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h index 0d2ce2f..194803f 100644 --- a/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h +++ b/avr/libraries/LottieLemon/src/utility/LottieLemonMotorBoard.h @@ -59,9 +59,9 @@ namespace LottieLemon { void stopCurrentActions(); //void sendCommand(byte command,byte codename,int value); - void _pinMode(uint8_t codename, int value); + void _pinMode(uint8_t codename, uint8_t value); void _analogWrite(uint8_t codename, int value); - void _digitalWrite(uint8_t codename, bool value); + void _digitalWrite(uint8_t codename, uint8_t value); void _analogRead(uint8_t codename); void _digitalRead(uint8_t codename); int _IRread(uint8_t num); From 410562337d51c3aa13a2a88d2a89225ca8697612 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 18 Mar 2017 12:27:39 +0100 Subject: [PATCH 61/67] Initalized RX packet variables to zero --- avr/libraries/IntegerEasyTransfer/README.txt | 1 + .../IntegerEasyTransfer/src/IntegerEasyTransfer.cpp | 10 +++++++++- .../IntegerEasyTransfer/src/IntegerEasyTransfer.h | 8 +++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/avr/libraries/IntegerEasyTransfer/README.txt b/avr/libraries/IntegerEasyTransfer/README.txt index 7f2b23b..9b3e4b7 100644 --- a/avr/libraries/IntegerEasyTransfer/README.txt +++ b/avr/libraries/IntegerEasyTransfer/README.txt @@ -49,6 +49,7 @@ * messages and software reset. * 1.1.1 * Added overloaded write method to TwoWayIntegerEasyTransfer. +* Initalized RX packet variables to zero. * * * Limits of the Library diff --git a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp index d28c73b..67758e8 100644 --- a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp +++ b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.cpp @@ -1,5 +1,5 @@ /******************************************************************************* -* IntegerEasyTransfer 1.0.0 library modified by Julian Sanin, sourced from: +* IntegerEasyTransfer 1.1.1 library modified by Julian Sanin, sourced from: * * EasyTransfer Arduino Library v2.1 * details and example sketch: @@ -44,6 +44,12 @@ * The library supports a maximum of 20 uint8_t or 10 int16_t values. * Mixed uint8_t and int16_t are allowed but care should be taken that the * values do not overflow the data buffer. +* 1.1.0 +* Added new classes to support two way communications and extended +* messages and software reset. +* 1.1.1 +* Added overloaded write method to TwoWayIntegerEasyTransfer. +* Initalized RX packet variables to zero. * * * Limits of the Library @@ -73,6 +79,8 @@ #include "IntegerEasyTransfer.h" void IntegerEasyTransfer::begin(Stream *theStream) { + _rx_len = 0; + _rx_array_inx = 0; _stream = theStream; _resetData(); diff --git a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h index 5384ec3..5f76e9e 100644 --- a/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h +++ b/avr/libraries/IntegerEasyTransfer/src/IntegerEasyTransfer.h @@ -1,5 +1,5 @@ /******************************************************************************* -* IntegerEasyTransfer 1.0.0 library modified by Julian Sanin, sourced from: +* IntegerEasyTransfer 1.1.1 library modified by Julian Sanin, sourced from: * * EasyTransfer Arduino Library v2.1 * details and example sketch: @@ -44,6 +44,12 @@ * The library supports a maximum of 20 uint8_t or 10 int16_t values. * Mixed uint8_t and int16_t are allowed but care should be taken that the * values do not overflow the data buffer. +* 1.1.0 +* Added new classes to support two way communications and extended +* messages and software reset. +* 1.1.1 +* Added overloaded write method to TwoWayIntegerEasyTransfer. +* Initalized RX packet variables to zero. * * * Limits of the Library From f43d118f1db5e73822eaf738df362ced2fa5a04c Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 18 Mar 2017 16:01:09 +0100 Subject: [PATCH 62/67] Changed system reset message value to avoid conflict with -1 --- .../IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h index 545051e..312da7f 100644 --- a/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h +++ b/avr/libraries/IntegerEasyTransfer/src/TwoWayIntegerEasyTransfer.h @@ -50,7 +50,7 @@ class CTwoWayIntegerEasyTransfer { /// Extended message for a feature request. MESSAGE_FEATURE = 0xF0, /// Extended message for software reset. - MESSAGE_SYSTEM_RESET = 0xFF + MESSAGE_SYSTEM_RESET = 0xF1 }; /// From b17c230b9106c69638aa9b07b1b1eff5618b7944 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 18 Mar 2017 17:35:13 +0100 Subject: [PATCH 63/67] Added unit tests to IntegerEasyTransfer library --- .../IntegerEasyTransfer.ino | 126 +++++++++++++ .../IntegerEasyTransfer/tests/README.md | 14 ++ .../TwoWayIntegerEasyTransfer.ino | 176 ++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino create mode 100644 avr/libraries/IntegerEasyTransfer/tests/README.md create mode 100644 avr/libraries/IntegerEasyTransfer/tests/TwoWayIntegerEasyTransfer/TwoWayIntegerEasyTransfer.ino diff --git a/avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino b/avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino new file mode 100644 index 0000000..522a1bd --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino @@ -0,0 +1,126 @@ +/* + * Unit test for IntegerEasyTransfer library. + * Run this test after adding changes to the library. To run this sketch start + * by downloading and installing the ArduinoUnit library into your sketchbook + * and then open the test example sketches of this library. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 +#include +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + Test::out = &Serial; +} + +void loop() { + Test::run(); +} + +const uint8_t DATA_TRASH{ 0x55 }; +const uint8_t HEADER1{ 0x06 }; +const uint8_t HEADER0{ 0x85 }; +const uint8_t DATA_SIZE{ 3 }; +const uint8_t DATA_BYTE{ 0x01 }; +const uint8_t DATA_INT1{ 0x02 }; +const uint8_t DATA_INT0{ 0x03 }; +const int16_t DATA_INT{ (DATA_INT1 << 8) | (DATA_INT0) }; +const uint8_t CRC{ DATA_SIZE ^ DATA_BYTE ^ DATA_INT1 ^ DATA_INT0 }; + +const char partialData[]{ + DATA_TRASH, + static_cast(HEADER1), static_cast(HEADER0), + 0 +}; + +const char fullData[]{ + static_cast(HEADER1), static_cast(HEADER0), + DATA_SIZE, DATA_BYTE, DATA_INT1, DATA_INT0, CRC, + 0 +}; + +test(IntegerEasyTransfer_receiveData) { + bool hasReceivedData; + FakeStreamBuffer fakeStreamBuffer; + IntegerEasyTransfer integerEasyTransfer; + + integerEasyTransfer.begin(&fakeStreamBuffer); + + fakeStreamBuffer.nextBytes(partialData); + hasReceivedData = integerEasyTransfer.receiveData(); + assertFalse(hasReceivedData); + fakeStreamBuffer.reset(); + + fakeStreamBuffer.nextBytes(fullData); + hasReceivedData = integerEasyTransfer.receiveData(); + assertTrue(hasReceivedData); + fakeStreamBuffer.reset(); + + assertEqual(integerEasyTransfer.readByte(), DATA_BYTE); + assertEqual(integerEasyTransfer.readInt(), DATA_INT); +} + +test(IntegerEasyTransfer_sendData) { + FakeStream fakeStream; + IntegerEasyTransfer integerEasyTransfer; + + integerEasyTransfer.begin(&fakeStream); + integerEasyTransfer.writeByte(DATA_BYTE); + integerEasyTransfer.writeInt(DATA_INT); + integerEasyTransfer.sendData(); + + assertEqual(fakeStream.bytesWritten(), fullData); +} + +const uint8_t DATA_BYTE_MIN{ 0 }; +const uint8_t DATA_BYTE_MAX{ 255 }; +const int16_t DATA_INT_MIN{ -32768 }; +const int16_t DATA_INT_MAX{ 32767 }; + +test(IntegerEasyTransfer_writeByte) { + FakeStream fakeStream; + IntegerEasyTransfer integerEasyTransfer; + + integerEasyTransfer.begin(&fakeStream); + integerEasyTransfer.writeByte(DATA_BYTE_MIN); + integerEasyTransfer.writeByte(DATA_BYTE_MAX); + + const char * expected{ "" }; + assertEqual(fakeStream.bytesWritten(), expected); +} + +test(IntegerEasyTransfer_writeInt) { + FakeStream fakeStream; + IntegerEasyTransfer integerEasyTransfer; + + integerEasyTransfer.begin(&fakeStream); + integerEasyTransfer.writeInt(DATA_INT_MIN); + integerEasyTransfer.writeInt(DATA_INT_MAX); + + const char * expected{ "" }; + assertEqual(fakeStream.bytesWritten(), expected); +} diff --git a/avr/libraries/IntegerEasyTransfer/tests/README.md b/avr/libraries/IntegerEasyTransfer/tests/README.md new file mode 100644 index 0000000..c816c64 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/tests/README.md @@ -0,0 +1,14 @@ +# Unit Testing IntegerEasyTransfer library + +Tests tests are written using the +[ArduinoUnit library version 2.2.0](https://github.com/mmurdoch/arduinounit). + +Follow the instructions in the ArduinoUnit readme to install the library. + +Compile and upload the test sketch as you would any other sketch. Then open the +Serial Monitor to view the test results. + +If you make changes to the IntegerEasyTransfer library, run the tests in /tests/ +to ensure that your changes have not produced any unexpected errors. + +You should also perform manual tests against actual hardware. diff --git a/avr/libraries/IntegerEasyTransfer/tests/TwoWayIntegerEasyTransfer/TwoWayIntegerEasyTransfer.ino b/avr/libraries/IntegerEasyTransfer/tests/TwoWayIntegerEasyTransfer/TwoWayIntegerEasyTransfer.ino new file mode 100644 index 0000000..0f48487 --- /dev/null +++ b/avr/libraries/IntegerEasyTransfer/tests/TwoWayIntegerEasyTransfer/TwoWayIntegerEasyTransfer.ino @@ -0,0 +1,176 @@ +/* + * Unit test for IntegerEasyTransfer library. + * Run this test after adding changes to the library. To run this sketch start + * by downloading and installing the ArduinoUnit library into your sketchbook + * and then open the test example sketches of this library. + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Julian Sanin + * + * 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 +#include +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + Test::out = &Serial; +} + +void loop() { + Test::run(); +} + +const uint8_t HEADER1{ 0x06 }; +const uint8_t HEADER0{ 0x85 }; +const uint8_t DATA_SIZE{ 3 }; +const uint8_t DATA_BYTE{ 0x01 }; +const uint8_t DATA_INT1{ 0x02 }; +const uint8_t DATA_INT0{ 0x03 }; +const int16_t DATA_INT{ (DATA_INT1 << 8) | (DATA_INT0) }; +const uint8_t CRC{ DATA_SIZE ^ DATA_BYTE ^ DATA_INT1 ^ DATA_INT0 }; + +const char fullData[]{ + static_cast(HEADER1), static_cast(HEADER0), + DATA_SIZE, DATA_BYTE, DATA_INT1, DATA_INT0, CRC, + 0 +}; + +test(TwoWayIntegerEasyTransfer_attachDefaultMessageCallback) { + static uint8_t receivedDataCmd; + static int16_t receivedDataArg; + FakeStreamBuffer fakeStreamBuffer; + + receivedDataCmd = 0x00; + receivedDataArg = 0x0000; + fakeStreamBuffer.nextBytes(fullData); + + TwoWayIntegerEasyTransfer.begin(&fakeStreamBuffer); + TwoWayIntegerEasyTransfer.attach( + [](uint8_t command, IntegerEasyTransfer & request) { + receivedDataCmd = command; + receivedDataArg = request.readInt(); + }); + + assertTrue(TwoWayIntegerEasyTransfer.hasReceivedData()); + + TwoWayIntegerEasyTransfer.processInput(); + fakeStreamBuffer.reset(); + + assertEqual(receivedDataCmd, DATA_BYTE); + assertEqual(receivedDataArg, DATA_INT); +} + +const uint8_t EXT_SIZE{ 2 }; +const uint8_t EXT_MSG{ TwoWayIntegerEasyTransfer.MESSAGE_FEATURE }; +const uint8_t CRC_EXT{ EXT_SIZE ^ EXT_MSG ^ DATA_BYTE }; + +const char extendedMessage[]{ + static_cast(HEADER1), static_cast(HEADER0), EXT_SIZE, + static_cast(EXT_MSG), DATA_BYTE, static_cast(CRC_EXT), + 0 +}; + +test(TwoWayIntegerEasyTransfer_attachExtendedMessageCallback) { + static uint8_t receivedDataArg; + FakeStreamBuffer fakeStreamBuffer; + + receivedDataArg = 0x0000; + fakeStreamBuffer.nextBytes(extendedMessage); + + TwoWayIntegerEasyTransfer.begin(&fakeStreamBuffer); + TwoWayIntegerEasyTransfer.attach([](IntegerEasyTransfer & request) { + receivedDataArg = request.readByte(); + }); + + assertTrue(TwoWayIntegerEasyTransfer.hasReceivedData()); + + TwoWayIntegerEasyTransfer.processInput(); + fakeStreamBuffer.reset(); + + assertEqual(receivedDataArg, DATA_BYTE); +} + +const uint8_t EXT_SYS_RESET_SIZE{ 1 }; +const uint8_t EXT_SYS_RESET_MSG{ + TwoWayIntegerEasyTransfer.MESSAGE_SYSTEM_RESET +}; +const uint8_t CRC_EXT_SYS_RESET{ EXT_SYS_RESET_SIZE ^ EXT_SYS_RESET_MSG }; + +const char extendedSysResetMessage[]{ + static_cast(HEADER1), static_cast(HEADER0), EXT_SYS_RESET_SIZE, + static_cast(EXT_SYS_RESET_MSG), static_cast(CRC_EXT_SYS_RESET), + 0 +}; + +test(TwoWayIntegerEasyTransfer_attachSystemResetCallback) { + static bool hasReceivedSystemReset; + FakeStreamBuffer fakeStreamBuffer; + + hasReceivedSystemReset = false; + fakeStreamBuffer.nextBytes(extendedSysResetMessage); + + TwoWayIntegerEasyTransfer.begin(&fakeStreamBuffer); + TwoWayIntegerEasyTransfer.attach([]() { + hasReceivedSystemReset = true; + }); + + assertTrue(TwoWayIntegerEasyTransfer.hasReceivedData()); + TwoWayIntegerEasyTransfer.processInput(); + assertTrue(hasReceivedSystemReset); + + fakeStreamBuffer.reset(); +} + +test(TwoWayIntegerEasyTransfer_sendDefaultMessage) { + FakeStream fakeStream; + + TwoWayIntegerEasyTransfer.begin(&fakeStream); + + TwoWayIntegerEasyTransfer.write(DATA_BYTE); + TwoWayIntegerEasyTransfer.write(DATA_INT); + TwoWayIntegerEasyTransfer.sendData(); + + assertEqual(fakeStream.bytesWritten(), fullData); +} + +test(TwoWayIntegerEasyTransfer_sendExtendedMessage) { + FakeStream fakeStream; + + TwoWayIntegerEasyTransfer.begin(&fakeStream); + + TwoWayIntegerEasyTransfer.write(EXT_MSG); + TwoWayIntegerEasyTransfer.write(DATA_BYTE); + TwoWayIntegerEasyTransfer.sendData(); + + assertEqual(fakeStream.bytesWritten(), extendedMessage); +} + +test(TwoWayIntegerEasyTransfer_sendSystemResetMessage) { + FakeStream fakeStream; + + TwoWayIntegerEasyTransfer.begin(&fakeStream); + TwoWayIntegerEasyTransfer.sendSystemReset(); + + assertEqual(fakeStream.bytesWritten(), extendedSysResetMessage); +} From 10690a17d0cb7aaab5a29b20bb24d2a37fde37af Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 18 Mar 2017 17:39:07 +0100 Subject: [PATCH 64/67] Bumped version in readme --- avr/libraries/IntegerEasyTransfer/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/libraries/IntegerEasyTransfer/README.txt b/avr/libraries/IntegerEasyTransfer/README.txt index 9b3e4b7..7ff0463 100644 --- a/avr/libraries/IntegerEasyTransfer/README.txt +++ b/avr/libraries/IntegerEasyTransfer/README.txt @@ -1,5 +1,5 @@ /******************************************************************************* -* IntegerEasyTransfer 1.1.0 library modified by Julian Sanin, sourced from: +* IntegerEasyTransfer 1.1.1 library modified by Julian Sanin, sourced from: * * EasyTransfer Arduino Library v2.1 * details and example sketch: From 96603248558d3c660aad5864148739fd6ed3bcf7 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Sat, 18 Mar 2017 18:15:45 +0100 Subject: [PATCH 65/67] Removed unused header --- .../tests/IntegerEasyTransfer/IntegerEasyTransfer.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino b/avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino index 522a1bd..673529e 100644 --- a/avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino +++ b/avr/libraries/IntegerEasyTransfer/tests/IntegerEasyTransfer/IntegerEasyTransfer.ino @@ -29,7 +29,6 @@ #include #include -#include void setup() { Serial.begin(9600); From 699910fa0c29941521c7ee47ca7a12760ca9f3a0 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Mon, 20 Mar 2017 10:21:08 +0100 Subject: [PATCH 66/67] Preparing release v1.0.4 --- avr/platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avr/platform.txt b/avr/platform.txt index d2d3291..30dbd5f 100644 --- a/avr/platform.txt +++ b/avr/platform.txt @@ -5,7 +5,7 @@ # - https://github.com/arduino/Arduino/wiki/Arduino-Hardware-Cores-migration-guide-from-1.0-to-1.6 name=Arduino Robot Boards -version=1.0.3 +version=1.0.4 # AVR compile variables # --------------------- From e745064fcbc9d0ed7489b150f7d421c9af6e40e3 Mon Sep 17 00:00:00 2001 From: Julian Sanin Date: Fri, 29 Sep 2017 12:11:53 +0200 Subject: [PATCH 67/67] Updated board manager URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6f69d8..67e5280 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ recommended and required. This installation method requires Arduino IDE version 1.6.11 or greater. * Open the Arduino IDE. * Open the **File > Preferences** menu item. -* Enter the following URL in **Additional Boards Manager URLs**: `https://j54n1n.github.io/arduinorobot/package_j54n1n_arduinorobot_index.json` +* Enter the following URL in **Additional Boards Manager URLs**: `https://rainerum-robotics-arduino.github.io/arduinorobot/package_j54n1n_arduinorobot_index.json` * Open the **Tools > Board > Boards Manager...** menu item. * Wait for the platform indexes to finish downloading. * Scroll down until you see the **Arduino Robot Boards** entry and click on it.