From e462bc214cc622d44e10e7738db369ee35267da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=B8rch?= Date: Thu, 5 Feb 2015 19:10:52 +0100 Subject: [PATCH 1/9] Added libraries for SPI flash, BMP180 and Si7021 --- libraries/SFE_BMP180/SFE_BMP180.cpp | 387 ++++++++++++++++++ libraries/SFE_BMP180/SFE_BMP180.h | 121 ++++++ .../BMP180_altitude_example.ino | 168 ++++++++ .../SFE_BMP180_example/SFE_BMP180_example.ino | 200 +++++++++ libraries/SFE_BMP180/keywords.txt | 27 ++ libraries/SI7021/LICENSE | 339 +++++++++++++++ libraries/SI7021/README.md | 4 + libraries/SI7021/SI7021.cpp | 135 ++++++ libraries/SI7021/SI7021.h | 51 +++ .../SI7021/examples/readsensor/readsensor.ino | 80 ++++ libraries/SPIFlash/README.md | 24 ++ libraries/SPIFlash/SPIFlash.cpp | 246 +++++++++++ libraries/SPIFlash/SPIFlash.h | 100 +++++ .../SPIFlash_ReadWrite/SPIFlash_ReadWrite.ino | 116 ++++++ libraries/SPIFlash/keywords.txt | 18 + libraries/SPIFlash/library.json | 12 + 16 files changed, 2028 insertions(+) create mode 100644 libraries/SFE_BMP180/SFE_BMP180.cpp create mode 100644 libraries/SFE_BMP180/SFE_BMP180.h create mode 100644 libraries/SFE_BMP180/examples/BMP180_altitude_example/BMP180_altitude_example.ino create mode 100644 libraries/SFE_BMP180/examples/SFE_BMP180_example/SFE_BMP180_example.ino create mode 100644 libraries/SFE_BMP180/keywords.txt create mode 100644 libraries/SI7021/LICENSE create mode 100644 libraries/SI7021/README.md create mode 100644 libraries/SI7021/SI7021.cpp create mode 100644 libraries/SI7021/SI7021.h create mode 100644 libraries/SI7021/examples/readsensor/readsensor.ino create mode 100644 libraries/SPIFlash/README.md create mode 100644 libraries/SPIFlash/SPIFlash.cpp create mode 100644 libraries/SPIFlash/SPIFlash.h create mode 100644 libraries/SPIFlash/examples/SPIFlash_ReadWrite/SPIFlash_ReadWrite.ino create mode 100644 libraries/SPIFlash/keywords.txt create mode 100644 libraries/SPIFlash/library.json diff --git a/libraries/SFE_BMP180/SFE_BMP180.cpp b/libraries/SFE_BMP180/SFE_BMP180.cpp new file mode 100644 index 0000000..4383fc5 --- /dev/null +++ b/libraries/SFE_BMP180/SFE_BMP180.cpp @@ -0,0 +1,387 @@ +/* + SFE_BMP180.cpp + Bosch BMP180 pressure sensor library for the Arduino microcontroller + Mike Grusin, SparkFun Electronics + + Uses floating-point equations from the Weather Station Data Logger project + http://wmrx00.sourceforge.net/ + http://wmrx00.sourceforge.net/Arduino/BMP085-Calcs.pdf + + Forked from BMP085 library by M.Grusin + + version 1.0 2013/09/20 initial version + + Our example code uses the "beerware" license. You can do anything + you like with this code. No really, anything. If you find it useful, + buy me a (root) beer someday. +*/ + +#include +#include +#include +#include + + +SFE_BMP180::SFE_BMP180() +// Base library type +{ +} + + +char SFE_BMP180::begin() +// Initialize library for subsequent pressure measurements +{ + double c3,c4,b1; + + // Start up the Arduino's "wire" (I2C) library: + + Wire.begin(); + + // The BMP180 includes factory calibration data stored on the device. + // Each device has different numbers, these must be retrieved and + // used in the calculations when taking pressure measurements. + + // Retrieve calibration data from device: + + if (readInt(0xAA,AC1) && + readInt(0xAC,AC2) && + readInt(0xAE,AC3) && + readUInt(0xB0,AC4) && + readUInt(0xB2,AC5) && + readUInt(0xB4,AC6) && + readInt(0xB6,VB1) && + readInt(0xB8,VB2) && + readInt(0xBA,MB) && + readInt(0xBC,MC) && + readInt(0xBE,MD)) + { + + // All reads completed successfully! + + // If you need to check your math using known numbers, + // you can uncomment one of these examples. + // (The correct results are commented in the below functions.) + + // Example from Bosch datasheet + // AC1 = 408; AC2 = -72; AC3 = -14383; AC4 = 32741; AC5 = 32757; AC6 = 23153; + // B1 = 6190; B2 = 4; MB = -32768; MC = -8711; MD = 2868; + + // Example from http://wmrx00.sourceforge.net/Arduino/BMP180-Calcs.pdf + // AC1 = 7911; AC2 = -934; AC3 = -14306; AC4 = 31567; AC5 = 25671; AC6 = 18974; + // VB1 = 5498; VB2 = 46; MB = -32768; MC = -11075; MD = 2432; + + /* + Serial.print("AC1: "); Serial.println(AC1); + Serial.print("AC2: "); Serial.println(AC2); + Serial.print("AC3: "); Serial.println(AC3); + Serial.print("AC4: "); Serial.println(AC4); + Serial.print("AC5: "); Serial.println(AC5); + Serial.print("AC6: "); Serial.println(AC6); + Serial.print("VB1: "); Serial.println(VB1); + Serial.print("VB2: "); Serial.println(VB2); + Serial.print("MB: "); Serial.println(MB); + Serial.print("MC: "); Serial.println(MC); + Serial.print("MD: "); Serial.println(MD); + */ + + // Compute floating-point polynominals: + + c3 = 160.0 * pow(2,-15) * AC3; + c4 = pow(10,-3) * pow(2,-15) * AC4; + b1 = pow(160,2) * pow(2,-30) * VB1; + c5 = (pow(2,-15) / 160) * AC5; + c6 = AC6; + mc = (pow(2,11) / pow(160,2)) * MC; + md = MD / 160.0; + x0 = AC1; + x1 = 160.0 * pow(2,-13) * AC2; + x2 = pow(160,2) * pow(2,-25) * VB2; + y0 = c4 * pow(2,15); + y1 = c4 * c3; + y2 = c4 * b1; + p0 = (3791.0 - 8.0) / 1600.0; + p1 = 1.0 - 7357.0 * pow(2,-20); + p2 = 3038.0 * 100.0 * pow(2,-36); + + /* + Serial.println(); + Serial.print("c3: "); Serial.println(c3); + Serial.print("c4: "); Serial.println(c4); + Serial.print("c5: "); Serial.println(c5); + Serial.print("c6: "); Serial.println(c6); + Serial.print("b1: "); Serial.println(b1); + Serial.print("mc: "); Serial.println(mc); + Serial.print("md: "); Serial.println(md); + Serial.print("x0: "); Serial.println(x0); + Serial.print("x1: "); Serial.println(x1); + Serial.print("x2: "); Serial.println(x2); + Serial.print("y0: "); Serial.println(y0); + Serial.print("y1: "); Serial.println(y1); + Serial.print("y2: "); Serial.println(y2); + Serial.print("p0: "); Serial.println(p0); + Serial.print("p1: "); Serial.println(p1); + Serial.print("p2: "); Serial.println(p2); + */ + + // Success! + return(1); + } + else + { + // Error reading calibration data; bad component or connection? + return(0); + } +} + + +char SFE_BMP180::readInt(char address, int &value) +// Read a signed integer (two bytes) from device +// address: register to start reading (plus subsequent register) +// value: external variable to store data (function modifies value) +{ + unsigned char data[2]; + + data[0] = address; + if (readBytes(data,2)) + { + value = (((int)data[0]<<8)|(int)data[1]); + //if (*value & 0x8000) *value |= 0xFFFF0000; // sign extend if negative + return(1); + } + value = 0; + return(0); +} + + +char SFE_BMP180::readUInt(char address, unsigned int &value) +// Read an unsigned integer (two bytes) from device +// address: register to start reading (plus subsequent register) +// value: external variable to store data (function modifies value) +{ + unsigned char data[2]; + + data[0] = address; + if (readBytes(data,2)) + { + value = (((unsigned int)data[0]<<8)|(unsigned int)data[1]); + return(1); + } + value = 0; + return(0); +} + + +char SFE_BMP180::readBytes(unsigned char *values, char length) +// Read an array of bytes from device +// values: external array to hold data. Put starting register in values[0]. +// length: number of bytes to read +{ + char x; + + Wire.beginTransmission(BMP180_ADDR); + Wire.write(values[0]); + _error = Wire.endTransmission(); + if (_error == 0) + { + Wire.requestFrom(BMP180_ADDR,length); + while(Wire.available() != length) ; // wait until bytes are ready + for(x=0;x= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +class SFE_BMP180 +{ + public: + SFE_BMP180(); // base type + + char begin(); + // call pressure.begin() to initialize BMP180 before use + // returns 1 if success, 0 if failure (bad component or I2C bus shorted?) + + char startTemperature(void); + // command BMP180 to start a temperature measurement + // returns (number of ms to wait) for success, 0 for fail + + char getTemperature(double &T); + // return temperature measurement from previous startTemperature command + // places returned value in T variable (deg C) + // returns 1 for success, 0 for fail + + char startPressure(char oversampling); + // command BMP180 to start a pressure measurement + // oversampling: 0 - 3 for oversampling value + // returns (number of ms to wait) for success, 0 for fail + + char getPressure(double &P, double &T); + // return absolute pressure measurement from previous startPressure command + // note: requires previous temperature measurement in variable T + // places returned value in P variable (mbar) + // returns 1 for success, 0 for fail + + double sealevel(double P, double A); + // convert absolute pressure to sea-level pressure (as used in weather data) + // P: absolute pressure (mbar) + // A: current altitude (meters) + // returns sealevel pressure in mbar + + double altitude(double P, double P0); + // convert absolute pressure to altitude (given baseline pressure; sea-level, runway, etc.) + // P: absolute pressure (mbar) + // P0: fixed baseline pressure (mbar) + // returns signed altitude in meters + + char getError(void); + // If any library command fails, you can retrieve an extended + // error code using this command. Errors are from the wire library: + // 0 = Success + // 1 = Data too long to fit in transmit buffer + // 2 = Received NACK on transmit of address + // 3 = Received NACK on transmit of data + // 4 = Other error + + private: + + char readInt(char address, int &value); + // read an signed int (16 bits) from a BMP180 register + // address: BMP180 register address + // value: external signed int for returned value (16 bits) + // returns 1 for success, 0 for fail, with result in value + + char readUInt(char address, unsigned int &value); + // read an unsigned int (16 bits) from a BMP180 register + // address: BMP180 register address + // value: external unsigned int for returned value (16 bits) + // returns 1 for success, 0 for fail, with result in value + + char readBytes(unsigned char *values, char length); + // read a number of bytes from a BMP180 register + // values: array of char with register address in first location [0] + // length: number of bytes to read back + // returns 1 for success, 0 for fail, with read bytes in values[] array + + char writeBytes(unsigned char *values, char length); + // write a number of bytes to a BMP180 register (and consecutive subsequent registers) + // values: array of char with register address in first location [0] + // length: number of bytes to write + // returns 1 for success, 0 for fail + + int AC1,AC2,AC3,VB1,VB2,MB,MC,MD; + unsigned int AC4,AC5,AC6; + double c5,c6,mc,md,x0,x1,x2,y0,y1,y2,p0,p1,p2; + char _error; +}; + +#define BMP180_ADDR 0x77 // 7-bit address + +#define BMP180_REG_CONTROL 0xF4 +#define BMP180_REG_RESULT 0xF6 + +#define BMP180_COMMAND_TEMPERATURE 0x2E +#define BMP180_COMMAND_PRESSURE0 0x34 +#define BMP180_COMMAND_PRESSURE1 0x74 +#define BMP180_COMMAND_PRESSURE2 0xB4 +#define BMP180_COMMAND_PRESSURE3 0xF4 + +#endif diff --git a/libraries/SFE_BMP180/examples/BMP180_altitude_example/BMP180_altitude_example.ino b/libraries/SFE_BMP180/examples/BMP180_altitude_example/BMP180_altitude_example.ino new file mode 100644 index 0000000..fdb8358 --- /dev/null +++ b/libraries/SFE_BMP180/examples/BMP180_altitude_example/BMP180_altitude_example.ino @@ -0,0 +1,168 @@ +/* SFE_BMP180 altitude example sketch + +This sketch shows how to use the Bosch BMP180 pressure sensor +as an altimiter. +https://www.sparkfun.com/products/11824 + +Like most pressure sensors, the BMP180 measures absolute pressure. +Since absolute pressure varies with altitude, you can use the pressure +to determine your altitude. + +Because pressure also varies with weather, you must first take a pressure +reading at a known baseline altitude. Then you can measure variations +from that pressure + +Hardware connections: + +- (GND) to GND ++ (VDD) to 3.3V + +(WARNING: do not connect + to 5V or the sensor will be damaged!) + +You will also need to connect the I2C pins (SCL and SDA) to your +Arduino. The pins are different on different Arduinos: + +Any Arduino pins labeled: SDA SCL +Uno, Redboard, Pro: A4 A5 +Mega2560, Due: 20 21 +Leonardo: 2 3 + +Leave the IO (VDDIO) pin unconnected. This pin is for connecting +the BMP180 to systems with lower logic levels such as 1.8V + +Have fun! -Your friends at SparkFun. + +The SFE_BMP180 library uses floating-point equations developed by the +Weather Station Data Logger project: http://wmrx00.sourceforge.net/ + +Our example code uses the "beerware" license. You can do anything +you like with this code. No really, anything. If you find it useful, +buy me a beer someday. + +V10 Mike Grusin, SparkFun Electronics 10/24/2013 +*/ + +// Your sketch must #include this library, and the Wire library. +// (Wire is a standard library included with Arduino.): + +#include +#include + +// You will need to create an SFE_BMP180 object, here called "pressure": + +SFE_BMP180 pressure; + +double baseline; // baseline pressure + +void setup() +{ + Serial.begin(9600); + Serial.println("REBOOT"); + + // Initialize the sensor (it is important to get calibration values stored on the device). + + if (pressure.begin()) + Serial.println("BMP180 init success"); + else + { + // Oops, something went wrong, this is usually a connection problem, + // see the comments at the top of this sketch for the proper connections. + + Serial.println("BMP180 init fail (disconnected?)\n\n"); + while(1); // Pause forever. + } + + // Get the baseline pressure: + + baseline = getPressure(); + + Serial.print("baseline pressure: "); + Serial.print(baseline); + Serial.println(" mb"); +} + +void loop() +{ + double a,P; + + // Get a new pressure reading: + + P = getPressure(); + + // Show the relative altitude difference between + // the new reading and the baseline reading: + + a = pressure.altitude(P,baseline); + + Serial.print("relative altitude: "); + if (a >= 0.0) Serial.print(" "); // add a space for positive numbers + Serial.print(a,1); + Serial.print(" meters, "); + if (a >= 0.0) Serial.print(" "); // add a space for positive numbers + Serial.print(a*3.28084,0); + Serial.println(" feet"); + + delay(500); +} + + +double getPressure() +{ + char status; + double T,P,p0,a; + + // You must first get a temperature measurement to perform a pressure reading. + + // Start a temperature measurement: + // If request is successful, the number of ms to wait is returned. + // If request is unsuccessful, 0 is returned. + + status = pressure.startTemperature(); + if (status != 0) + { + // Wait for the measurement to complete: + + delay(status); + + // Retrieve the completed temperature measurement: + // Note that the measurement is stored in the variable T. + // Use '&T' to provide the address of T to the function. + // Function returns 1 if successful, 0 if failure. + + status = pressure.getTemperature(T); + if (status != 0) + { + // Start a pressure measurement: + // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait). + // If request is successful, the number of ms to wait is returned. + // If request is unsuccessful, 0 is returned. + + status = pressure.startPressure(3); + if (status != 0) + { + // Wait for the measurement to complete: + delay(status); + + // Retrieve the completed pressure measurement: + // Note that the measurement is stored in the variable P. + // Use '&P' to provide the address of P. + // Note also that the function requires the previous temperature measurement (T). + // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.) + // Function returns 1 if successful, 0 if failure. + + status = pressure.getPressure(P,T); + if (status != 0) + { + return(P); + } + else Serial.println("error retrieving pressure measurement\n"); + } + else Serial.println("error starting pressure measurement\n"); + } + else Serial.println("error retrieving temperature measurement\n"); + } + else Serial.println("error starting temperature measurement\n"); +} + + + diff --git a/libraries/SFE_BMP180/examples/SFE_BMP180_example/SFE_BMP180_example.ino b/libraries/SFE_BMP180/examples/SFE_BMP180_example/SFE_BMP180_example.ino new file mode 100644 index 0000000..a43c905 --- /dev/null +++ b/libraries/SFE_BMP180/examples/SFE_BMP180_example/SFE_BMP180_example.ino @@ -0,0 +1,200 @@ +/* SFE_BMP180 library example sketch + +This sketch shows how to use the SFE_BMP180 library to read the +Bosch BMP180 barometric pressure sensor. +https://www.sparkfun.com/products/11824 + +Like most pressure sensors, the BMP180 measures absolute pressure. +This is the actual ambient pressure seen by the device, which will +vary with both altitude and weather. + +Before taking a pressure reading you must take a temparture reading. +This is done with startTemperature() and getTemperature(). +The result is in degrees C. + +Once you have a temperature reading, you can take a pressure reading. +This is done with startPressure() and getPressure(). +The result is in millibar (mb) aka hectopascals (hPa). + +If you'll be monitoring weather patterns, you will probably want to +remove the effects of altitude. This will produce readings that can +be compared to the published pressure readings from other locations. +To do this, use the sealevel() function. You will need to provide +the known altitude at which the pressure was measured. + +If you want to measure altitude, you will need to know the pressure +at a baseline altitude. This can be average sealevel pressure, or +a previous pressure reading at your altitude, in which case +subsequent altitude readings will be + or - the initial baseline. +This is done with the altitude() function. + +Hardware connections: + +- (GND) to GND ++ (VDD) to 3.3V + +(WARNING: do not connect + to 5V or the sensor will be damaged!) + +You will also need to connect the I2C pins (SCL and SDA) to your +Arduino. The pins are different on different Arduinos: + +Any Arduino pins labeled: SDA SCL +Uno, Redboard, Pro: A4 A5 +Mega2560, Due: 20 21 +Leonardo: 2 3 + +Leave the IO (VDDIO) pin unconnected. This pin is for connecting +the BMP180 to systems with lower logic levels such as 1.8V + +Have fun! -Your friends at SparkFun. + +The SFE_BMP180 library uses floating-point equations developed by the +Weather Station Data Logger project: http://wmrx00.sourceforge.net/ + +Our example code uses the "beerware" license. You can do anything +you like with this code. No really, anything. If you find it useful, +buy me a beer someday. + +V10 Mike Grusin, SparkFun Electronics 10/24/2013 +*/ + +// Your sketch must #include this library, and the Wire library. +// (Wire is a standard library included with Arduino.): + +#include +#include + +// You will need to create an SFE_BMP180 object, here called "pressure": + +SFE_BMP180 pressure; + +#define ALTITUDE 1655.0 // Altitude of SparkFun's HQ in Boulder, CO. in meters + +void setup() +{ + Serial.begin(9600); + Serial.println("REBOOT"); + + // Initialize the sensor (it is important to get calibration values stored on the device). + + if (pressure.begin()) + Serial.println("BMP180 init success"); + else + { + // Oops, something went wrong, this is usually a connection problem, + // see the comments at the top of this sketch for the proper connections. + + Serial.println("BMP180 init fail\n\n"); + while(1); // Pause forever. + } +} + +void loop() +{ + char status; + double T,P,p0,a; + + // Loop here getting pressure readings every 10 seconds. + + // If you want sea-level-compensated pressure, as used in weather reports, + // you will need to know the altitude at which your measurements are taken. + // We're using a constant called ALTITUDE in this sketch: + + Serial.println(); + Serial.print("provided altitude: "); + Serial.print(ALTITUDE,0); + Serial.print(" meters, "); + Serial.print(ALTITUDE*3.28084,0); + Serial.println(" feet"); + + // If you want to measure altitude, and not pressure, you will instead need + // to provide a known baseline pressure. This is shown at the end of the sketch. + + // You must first get a temperature measurement to perform a pressure reading. + + // Start a temperature measurement: + // If request is successful, the number of ms to wait is returned. + // If request is unsuccessful, 0 is returned. + + status = pressure.startTemperature(); + if (status != 0) + { + // Wait for the measurement to complete: + delay(status); + + // Retrieve the completed temperature measurement: + // Note that the measurement is stored in the variable T. + // Function returns 1 if successful, 0 if failure. + + status = pressure.getTemperature(T); + if (status != 0) + { + // Print out the measurement: + Serial.print("temperature: "); + Serial.print(T,2); + Serial.print(" deg C, "); + Serial.print((9.0/5.0)*T+32.0,2); + Serial.println(" deg F"); + + // Start a pressure measurement: + // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait). + // If request is successful, the number of ms to wait is returned. + // If request is unsuccessful, 0 is returned. + + status = pressure.startPressure(3); + if (status != 0) + { + // Wait for the measurement to complete: + delay(status); + + // Retrieve the completed pressure measurement: + // Note that the measurement is stored in the variable P. + // Note also that the function requires the previous temperature measurement (T). + // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.) + // Function returns 1 if successful, 0 if failure. + + status = pressure.getPressure(P,T); + if (status != 0) + { + // Print out the measurement: + Serial.print("absolute pressure: "); + Serial.print(P,2); + Serial.print(" mb, "); + Serial.print(P*0.0295333727,2); + Serial.println(" inHg"); + + // The pressure sensor returns abolute pressure, which varies with altitude. + // To remove the effects of altitude, use the sealevel function and your current altitude. + // This number is commonly used in weather reports. + // Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m. + // Result: p0 = sea-level compensated pressure in mb + + p0 = pressure.sealevel(P,ALTITUDE); // we're at 1655 meters (Boulder, CO) + Serial.print("relative (sea-level) pressure: "); + Serial.print(p0,2); + Serial.print(" mb, "); + Serial.print(p0*0.0295333727,2); + Serial.println(" inHg"); + + // On the other hand, if you want to determine your altitude from the pressure reading, + // use the altitude function along with a baseline pressure (sea-level or other). + // Parameters: P = absolute pressure in mb, p0 = baseline pressure in mb. + // Result: a = altitude in m. + + a = pressure.altitude(P,p0); + Serial.print("computed altitude: "); + Serial.print(a,0); + Serial.print(" meters, "); + Serial.print(a*3.28084,0); + Serial.println(" feet"); + } + else Serial.println("error retrieving pressure measurement\n"); + } + else Serial.println("error starting pressure measurement\n"); + } + else Serial.println("error retrieving temperature measurement\n"); + } + else Serial.println("error starting temperature measurement\n"); + + delay(5000); // Pause for 5 seconds. +} diff --git a/libraries/SFE_BMP180/keywords.txt b/libraries/SFE_BMP180/keywords.txt new file mode 100644 index 0000000..664167d --- /dev/null +++ b/libraries/SFE_BMP180/keywords.txt @@ -0,0 +1,27 @@ +####################################### +# Syntax Coloring Map for SFE_BMP180 +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SFE_BMP180 KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +startTemperature KEYWORD2 +getTemperature KEYWORD2 +startPressure KEYWORD2 +getPressure KEYWORD2 +sealevel KEYWORD2 +altitude KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +BMP180_ADDR LITERAL1 \ No newline at end of file diff --git a/libraries/SI7021/LICENSE b/libraries/SI7021/LICENSE new file mode 100644 index 0000000..d7f1051 --- /dev/null +++ b/libraries/SI7021/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + 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 2 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/libraries/SI7021/README.md b/libraries/SI7021/README.md new file mode 100644 index 0000000..38f42bf --- /dev/null +++ b/libraries/SI7021/README.md @@ -0,0 +1,4 @@ +SI7021 +====== + +Arduino library for SI7020 and SI7021 environmental sensors diff --git a/libraries/SI7021/SI7021.cpp b/libraries/SI7021/SI7021.cpp new file mode 100644 index 0000000..2bde080 --- /dev/null +++ b/libraries/SI7021/SI7021.cpp @@ -0,0 +1,135 @@ +/* +Copyright 2014 Marcus Sorensen + +This program is licensed under the GNU GPL v2 +*/ +#include "Arduino.h" +#include "SI7021.h" +#include + +#define I2C_ADDR 0x40 + +// I2C commands +byte RH_READ[] = { 0xE5 }; +byte TEMP_READ[] = { 0xE3 }; +byte POST_RH_TEMP_READ[] = { 0xE0 }; +byte RESET[] = { 0xFE }; +byte USER1_READ[] = { 0xE7 }; +byte USER1_WRITE[] = { 0xE6 }; +byte SERIAL1_READ[] = { 0xFA, 0x0F }; +byte SERIAL2_READ[] = { 0xFC, 0xC9 }; + +bool _si_exists = false; + +SI7021::SI7021() { +} + +bool SI7021::begin() { + Wire.begin(); + Wire.beginTransmission(I2C_ADDR); + if (Wire.endTransmission() == 0) { + _si_exists = true; + } + return _si_exists; +} + +bool SI7021::sensorExists() { + return _si_exists; +} + +int SI7021::getFahrenheitHundredths() { + unsigned int c = getCelsiusHundredths(); + return (1.8 * c) + 3200; +} + +int SI7021::getCelsiusHundredths() { + byte tempbytes[2]; + _command(TEMP_READ, tempbytes); + long tempraw = (long)tempbytes[0] << 8 | tempbytes[1]; + return ((17572 * tempraw) >> 16) - 4685; +} + +int SI7021::_getCelsiusPostHumidity() { + byte tempbytes[2]; + _command(POST_RH_TEMP_READ, tempbytes); + long tempraw = (long)tempbytes[0] << 8 | tempbytes[1]; + return ((17572 * tempraw) >> 16) - 4685; +} + + +unsigned int SI7021::getHumidityPercent() { + byte humbytes[2]; + _command(RH_READ, humbytes); + long humraw = (long)humbytes[0] << 8 | humbytes[1]; + return ((125 * humraw) >> 16) - 6; +} + +unsigned int SI7021::getHumidityBasisPoints() { + byte humbytes[2]; + _command(RH_READ, humbytes); + long humraw = (long)humbytes[0] << 8 | humbytes[1]; + return ((12500 * humraw) >> 16) - 600; +} + +void SI7021::_command(byte * cmd, byte * buf ) { + _writeReg(cmd, sizeof cmd); + _readReg(buf, sizeof buf); +} + +void SI7021::_writeReg(byte * reg, int reglen) { + Wire.beginTransmission(I2C_ADDR); + for(int i = 0; i < reglen; i++) { + reg += i; + Wire.write(*reg); + } + Wire.endTransmission(); +} + +int SI7021::_readReg(byte * reg, int reglen) { + Wire.requestFrom(I2C_ADDR, reglen); + while(Wire.available() < reglen) { + } + for(int i = 0; i < reglen; i++) { + reg[i] = Wire.read(); + } + return 1; +} + +//note this has crc bytes embedded, per datasheet, so provide 12 byte buf +int SI7021::getSerialBytes(byte * buf) { + _writeReg(SERIAL1_READ, sizeof SERIAL1_READ); + _readReg(buf, 6); + + _writeReg(SERIAL2_READ, sizeof SERIAL2_READ); + _readReg(buf + 6, 6); + + // could verify crc here and return only the 8 bytes that matter + return 1; +} + +int SI7021::getDeviceId() { + byte serial[12]; + getSerialBytes(serial); + int id = serial[6]; + return id; +} + +void SI7021::setHeater(bool on) { + byte userbyte; + if (on) { + userbyte = 0x3E; + } else { + userbyte = 0x3A; + } + byte userwrite[] = {USER1_WRITE[0], userbyte}; + _writeReg(userwrite, sizeof userwrite); +} + +// get humidity, then get temperature reading from humidity measurement +struct si7021_env SI7021::getHumidityAndTemperature() { + si7021_env ret = {0, 0, 0}; + ret.humidityBasisPoints = getHumidityBasisPoints(); + ret.celsiusHundredths = _getCelsiusPostHumidity(); + ret.fahrenheitHundredths = (1.8 * ret.celsiusHundredths) + 3200; + return ret; +} diff --git a/libraries/SI7021/SI7021.h b/libraries/SI7021/SI7021.h new file mode 100644 index 0000000..5aa9f4b --- /dev/null +++ b/libraries/SI7021/SI7021.h @@ -0,0 +1,51 @@ +/* + Copyright 2014 Marcus Sorensen + +This program is licensed under the GNU GPL v2 +*/ +#ifndef si7021_h +#define si7021_h + +#if (ARDUINO >= 100) + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#ifdef __AVR_ATtiny85__ + #include "TinyWireM.h" + #define Wire TinyWireM +#else + #include + #include +#endif + +typedef struct si7021_env { + int celsiusHundredths; + int fahrenheitHundredths; + unsigned int humidityBasisPoints; +} si7021_env; + +class SI7021 +{ + public: + SI7021(); + bool begin(); + bool sensorExists(); + int getFahrenheitHundredths(); + int getCelsiusHundredths(); + unsigned int getHumidityPercent(); + unsigned int getHumidityBasisPoints(); + struct si7021_env getHumidityAndTemperature(); + int getSerialBytes(byte * buf); + int getDeviceId(); + void setHeater(bool on); + private: + void _command(byte * cmd, byte * buf ); + void _writeReg(byte * reg, int reglen); + int _readReg(byte * reg, int reglen); + int _getCelsiusPostHumidity(); +}; + +#endif + diff --git a/libraries/SI7021/examples/readsensor/readsensor.ino b/libraries/SI7021/examples/readsensor/readsensor.ino new file mode 100644 index 0000000..95194cf --- /dev/null +++ b/libraries/SI7021/examples/readsensor/readsensor.ino @@ -0,0 +1,80 @@ +#include +#include + + +SI7021 sensor; +int led1 = 3; +int led2 = 4; + +void setup() { + pinMode(led1, OUTPUT); + pinMode(led2, OUTPUT); + sensor.begin(); +} + + +void loop() { + + // temperature is an integer in hundredths + int temperature = sensor.getCelsiusHundredths(); + temperature = temperature / 100; + for (int i = 0; i < temperature; i++) { + pulse(led1); + } + + delay(5000); + + // humidity is an integer representing percent + int humidity = sensor.getHumidityPercent(); + for (int i = 0; i < humidity; i++) { + pulse(led2); + } + + delay(5000); + + // this driver should work for SI7020 and SI7021, this returns 20 or 21 + int deviceid = sensor.getDeviceId(); + for (int i = 0; i < deviceid; i++) { + pulse(led1); + } + delay(5000); + + // enable internal heater for testing + sensor.setHeater(true); + delay(20000); + sensor.setHeater(false); + + // see if heater changed temperature + int temperature = sensor.getCelsiusHundredths(); + temperature = temperature / 100; + for (int i = 0; i < temperature; i++) { + pulse(led2); + } + + //cool down + delay(20000); + + // get humidity and temperature in one shot, saves power because sensor takes temperature when doing humidity anyway + si7021_env data = sensor.getHumidityAndTemperature(); + for (int i = 0; i < data.celsiusHundredths/100; i++) { + pulse(led1); + } + for (int i = 0; i < data.humidityPercent; i++) { + pulse(led2); + } + delay(5000); +} + +void pulse(int pin) { + // software pwm, flash light for ~1 second with short duty cycle + for (int i = 0; i < 20; i++) { + digitalWrite(pin, HIGH); + delay(1); + digitalWrite(pin,LOW); + delay(9); + } + digitalWrite(pin,LOW); + delay(300); +} + + diff --git a/libraries/SPIFlash/README.md b/libraries/SPIFlash/README.md new file mode 100644 index 0000000..63a1cac --- /dev/null +++ b/libraries/SPIFlash/README.md @@ -0,0 +1,24 @@ +SPIFlash +======== +Arduino/Moteino library for read/write access to SPI flash memory chips. +This works with 256byte/page SPI flash memory such as the 4MBIT W25X40CLSNIG used on (Moteinos)[www.moteino.com] for data storage and wireless programming. +
+For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes). +
Minimal modifications should allow chips that have different page size to work. +
DEPENDS ON: Arduino *SPI library*. +
+This library was primarily developed to enable **safe** wireless programming on Moteino nodes and Moteino based applications such as the SwitchMote. This has been documented at [lowpowerlab](http://lowpowerlab.com/blog/category/moteino/wireless-programming/). [Dualoptiboot](https://github.com/LowPowerLab/DualOptiboot) (all Moteinos come with it) and [WirelessProgramming library](https://github.com/LowPowerLab/WirelessProgramming) are required to be able to wirelessly re-flash a remote Moteino. + +###Installation +Copy the content of this library in the "Arduino/libraries/SPIFlash" folder. +
+To find your Arduino folder go to File>Preferences in the Arduino IDE. +
+See [this tutorial](http://learn.adafruit.com/arduino-tips-tricks-and-techniques/arduino-libraries) on Arduino libraries. + +###License +Copyright (c) 2013 by Felix Rusu +
+This library is free software; you can redistribute it and/or modify it under the terms of either the GNU General Public License version 2 or the GNU Lesser General Public License version 2.1, both as published by the Free Software Foundation. +
+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. diff --git a/libraries/SPIFlash/SPIFlash.cpp b/libraries/SPIFlash/SPIFlash.cpp new file mode 100644 index 0000000..03d6a1a --- /dev/null +++ b/libraries/SPIFlash/SPIFlash.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2013 by Felix Rusu + * SPI Flash memory library for arduino/moteino. + * This works with 256byte/page SPI flash memory + * For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes) + * Minimal modifications should allow chips that have different page size but modifications + * DEPENDS ON: Arduino SPI library + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#include + +byte SPIFlash::UNIQUEID[8]; + +/// IMPORTANT: NAND FLASH memory requires erase before write, because +/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s +/// See http://en.wikipedia.org/wiki/Flash_memory +/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command + +/// Constructor. JedecID is optional but recommended, since this will ensure that the device is present and has a valid response +/// get this from the datasheet of your flash chip +/// Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf) +/// Example for Winbond 4Mbit W25X40CL: 0xEF30 (page 14: http://www.winbond.com/NR/rdonlyres/6E25084C-0BFE-4B25-903D-AE10221A0929/0/W25X40CL.pdf) +SPIFlash::SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID) { + _slaveSelectPin = slaveSelectPin; + _jedecID = jedecID; +} + +/// Select the flash chip +void SPIFlash::select() { + noInterrupts(); + //save current SPI settings + _SPCR = SPCR; + _SPSR = SPSR; + //set FLASH chip SPI settings + SPI.setDataMode(SPI_MODE0); + SPI.setBitOrder(MSBFIRST); + SPI.setClockDivider(SPI_CLOCK_DIV4); //decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present + SPI.begin(); + digitalWrite(_slaveSelectPin, LOW); +} + +/// UNselect the flash chip +void SPIFlash::unselect() { + digitalWrite(_slaveSelectPin, HIGH); + //restore SPI settings to what they were before talking to the FLASH chip + SPCR = _SPCR; + SPSR = _SPSR; + interrupts(); +} + +/// setup SPI, read device ID etc... +boolean SPIFlash::initialize() +{ + _SPCR = SPCR; + _SPSR = SPSR; + pinMode(_slaveSelectPin, OUTPUT); + unselect(); + wakeup(); + + if (_jedecID == 0 || readDeviceId() == _jedecID) { + command(SPIFLASH_STATUSWRITE, true); // Write Status Register + SPI.transfer(0); // Global Unprotect + unselect(); + return true; + } + return false; +} + +/// Get the manufacturer and device ID bytes (as a short word) +word SPIFlash::readDeviceId() +{ +#if defined(__AVR_ATmega32U4__) // Arduino Leonardo, MoteinoLeo + command(SPIFLASH_IDREAD); // Read JEDEC ID +#else + select(); + SPI.transfer(SPIFLASH_IDREAD); +#endif + word jedecid = SPI.transfer(0) << 8; + jedecid |= SPI.transfer(0); + unselect(); + return jedecid; +} + +/// Get the 64 bit unique identifier, stores it in UNIQUEID[8]. Only needs to be called once, ie after initialize +/// Returns the byte pointer to the UNIQUEID byte array +/// Read UNIQUEID like this: +/// flash.readUniqueId(); for (byte i=0;i<8;i++) { Serial.print(flash.UNIQUEID[i], HEX); Serial.print(' '); } +/// or like this: +/// flash.readUniqueId(); byte* MAC = flash.readUniqueId(); for (byte i=0;i<8;i++) { Serial.print(MAC[i], HEX); Serial.print(' '); } +byte* SPIFlash::readUniqueId() +{ + command(SPIFLASH_MACREAD); + SPI.transfer(0); + SPI.transfer(0); + SPI.transfer(0); + SPI.transfer(0); + for (byte i=0;i<8;i++) + UNIQUEID[i] = SPI.transfer(0); + unselect(); + return UNIQUEID; +} + +/// read 1 byte from flash memory +byte SPIFlash::readByte(long addr) { + command(SPIFLASH_ARRAYREADLOWFREQ); + SPI.transfer(addr >> 16); + SPI.transfer(addr >> 8); + SPI.transfer(addr); + byte result = SPI.transfer(0); + unselect(); + return result; +} + +/// read unlimited # of bytes +void SPIFlash::readBytes(long addr, void* buf, word len) { + command(SPIFLASH_ARRAYREAD); + SPI.transfer(addr >> 16); + SPI.transfer(addr >> 8); + SPI.transfer(addr); + SPI.transfer(0); //"dont care" + for (word i = 0; i < len; ++i) + ((byte*) buf)[i] = SPI.transfer(0); + unselect(); +} + +/// Send a command to the flash chip, pass TRUE for isWrite when its a write command +void SPIFlash::command(byte cmd, boolean isWrite){ +#if defined(__AVR_ATmega32U4__) // Arduino Leonardo, MoteinoLeo + DDRB |= B00000001; // Make sure the SS pin (PB0 - used by RFM12B on MoteinoLeo R1) is set as output HIGH! + PORTB |= B00000001; +#endif + if (isWrite) + { + command(SPIFLASH_WRITEENABLE); // Write Enable + unselect(); + } + //wait for any write/erase to complete + // a time limit cannot really be added here without it being a very large safe limit + // that is because some chips can take several seconds to carry out a chip erase or other similar multi block or entire-chip operations + // a recommended alternative to such situations where chip can be or not be present is to add a 10k or similar weak pulldown on the + // open drain MISO input which can read noise/static and hence return a non 0 status byte, causing the while() to hang when a flash chip is not present + while(busy()); + select(); + SPI.transfer(cmd); +} + +/// check if the chip is busy erasing/writing +boolean SPIFlash::busy() +{ + /* + select(); + SPI.transfer(SPIFLASH_STATUSREAD); + byte status = SPI.transfer(0); + unselect(); + return status & 1; + */ + return readStatus() & 1; +} + +/// return the STATUS register +byte SPIFlash::readStatus() +{ + select(); + SPI.transfer(SPIFLASH_STATUSREAD); + byte status = SPI.transfer(0); + unselect(); + return status; +} + + +/// Write 1 byte to flash memory +/// WARNING: you can only write to previously erased memory locations (see datasheet) +/// use the block erase commands to first clear memory (write 0xFFs) +void SPIFlash::writeByte(long addr, uint8_t byt) { + command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program + SPI.transfer(addr >> 16); + SPI.transfer(addr >> 8); + SPI.transfer(addr); + SPI.transfer(byt); + unselect(); +} + +/// write 1-256 bytes to flash memory +/// WARNING: you can only write to previously erased memory locations (see datasheet) +/// use the block erase commands to first clear memory (write 0xFFs) +/// WARNING: if you write beyond a page boundary (or more than 256bytes), +/// the bytes will wrap around and start overwriting at the beginning of that same page +/// see datasheet for more details +void SPIFlash::writeBytes(long addr, const void* buf, uint16_t len) { + command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program + SPI.transfer(addr >> 16); + SPI.transfer(addr >> 8); + SPI.transfer(addr); + for (uint16_t i = 0; i < len; i++) + SPI.transfer(((byte*) buf)[i]); + unselect(); +} + +/// erase entire flash memory array +/// may take several seconds depending on size, but is non blocking +/// so you may wait for this to complete using busy() or continue doing +/// other things and later check if the chip is done with busy() +/// note that any command will first wait for chip to become available using busy() +/// so no need to do that twice +void SPIFlash::chipErase() { + command(SPIFLASH_CHIPERASE, true); + unselect(); +} + +/// erase a 4Kbyte block +void SPIFlash::blockErase4K(long addr) { + command(SPIFLASH_BLOCKERASE_4K, true); // Block Erase + SPI.transfer(addr >> 16); + SPI.transfer(addr >> 8); + SPI.transfer(addr); + unselect(); +} + +/// erase a 32Kbyte block +void SPIFlash::blockErase32K(long addr) { + command(SPIFLASH_BLOCKERASE_32K, true); // Block Erase + SPI.transfer(addr >> 16); + SPI.transfer(addr >> 8); + SPI.transfer(addr); + unselect(); +} + +void SPIFlash::sleep() { + command(SPIFLASH_SLEEP); + unselect(); +} + +void SPIFlash::wakeup() { + command(SPIFLASH_WAKE); + unselect(); +} + +/// cleanup +void SPIFlash::end() { + SPI.end(); +} \ No newline at end of file diff --git a/libraries/SPIFlash/SPIFlash.h b/libraries/SPIFlash/SPIFlash.h new file mode 100644 index 0000000..f0c071a --- /dev/null +++ b/libraries/SPIFlash/SPIFlash.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2013 by Felix Rusu + * SPI Flash memory library for arduino/moteino. + * This works with 256byte/page SPI flash memory + * For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes) + * Minimal modifications should allow chips that have different page size but modifications + * DEPENDS ON: Arduino SPI library + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#ifndef _SPIFLASH_H_ +#define _SPIFLASH_H_ + +#if ARDUINO >= 100 +#include +#else +#include +#include "pins_arduino.h" +#endif + +#include + +/// IMPORTANT: NAND FLASH memory requires erase before write, because +/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s +/// See http://en.wikipedia.org/wiki/Flash_memory +/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command + +/// Standard SPI flash commands +/// Assuming the WP pin is pulled up (to disable hardware write protection) +/// To use any write commands the WEL bit in the status register must be set to 1. +/// This is accomplished by sending a 0x06 command before any such write/erase command. +/// The WEL bit in the status register resets to the logical “0” state after a +/// device power-up or reset. In addition, the WEL bit will be reset to the logical “0” state automatically under the following conditions: +/// • Write Disable operation completes successfully +/// • Write Status Register operation completes successfully or aborts +/// • Protect Sector operation completes successfully or aborts +/// • Unprotect Sector operation completes successfully or aborts +/// • Byte/Page Program operation completes successfully or aborts +/// • Sequential Program Mode reaches highest unprotected memory location +/// • Sequential Program Mode reaches the end of the memory array +/// • Sequential Program Mode aborts +/// • Block Erase operation completes successfully or aborts +/// • Chip Erase operation completes successfully or aborts +/// • Hold condition aborts +#define SPIFLASH_WRITEENABLE 0x06 // write enable +#define SPIFLASH_WRITEDISABLE 0x04 // write disable + +#define SPIFLASH_BLOCKERASE_4K 0x20 // erase one 4K block of flash memory +#define SPIFLASH_BLOCKERASE_32K 0x52 // erase one 32K block of flash memory +#define SPIFLASH_BLOCKERASE_64K 0xD8 // erase one 64K block of flash memory +#define SPIFLASH_CHIPERASE 0x60 // chip erase (may take several seconds depending on size) + // but no actual need to wait for completion (instead need to check the status register BUSY bit) +#define SPIFLASH_STATUSREAD 0x05 // read status register +#define SPIFLASH_STATUSWRITE 0x01 // write status register +#define SPIFLASH_ARRAYREAD 0x0B // read array (fast, need to add 1 dummy byte after 3 address bytes) +#define SPIFLASH_ARRAYREADLOWFREQ 0x03 // read array (low frequency) + +#define SPIFLASH_SLEEP 0xB9 // deep power down +#define SPIFLASH_WAKE 0xAB // deep power wake up +#define SPIFLASH_BYTEPAGEPROGRAM 0x02 // write (1 to 256bytes) +#define SPIFLASH_IDREAD 0x9F // read JEDEC manufacturer and device ID (2 bytes, specific bytes for each manufacturer and device) + // Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf) + // Example for Winbond 4Mbit W25X40CL: 0xEF30 (page 14: http://www.winbond.com/NR/rdonlyres/6E25084C-0BFE-4B25-903D-AE10221A0929/0/W25X40CL.pdf) +#define SPIFLASH_MACREAD 0x4B // read unique ID number (MAC) + +class SPIFlash { +public: + static byte UNIQUEID[8]; + SPIFlash(byte slaveSelectPin, uint16_t jedecID=0); + boolean initialize(); + void command(byte cmd, boolean isWrite=false); + byte readStatus(); + byte readByte(long addr); + void readBytes(long addr, void* buf, word len); + void writeByte(long addr, byte byt); + void writeBytes(long addr, const void* buf, uint16_t len); + boolean busy(); + void chipErase(); + void blockErase4K(long address); + void blockErase32K(long address); + word readDeviceId(); + byte* readUniqueId(); + + void sleep(); + void wakeup(); + void end(); +protected: + void select(); + void unselect(); + byte _slaveSelectPin; + uint16_t _jedecID; + byte _SPCR; + byte _SPSR; +}; + +#endif \ No newline at end of file diff --git a/libraries/SPIFlash/examples/SPIFlash_ReadWrite/SPIFlash_ReadWrite.ino b/libraries/SPIFlash/examples/SPIFlash_ReadWrite/SPIFlash_ReadWrite.ino new file mode 100644 index 0000000..dd8d0e8 --- /dev/null +++ b/libraries/SPIFlash/examples/SPIFlash_ReadWrite/SPIFlash_ReadWrite.ino @@ -0,0 +1,116 @@ +// ********************************************************************************** +// This sketch is an example of using the SPIFlash library with a Moteino +// that has an onboard SPI Flash chip. This sketch listens to a few serial commands +// Hence type the following commands to interact with the SPI flash memory array: +// - 'd' dumps the first 256bytes of the flash chip to screen +// - 'e' erases the entire memory chip +// - 'i' print manufacturer/device ID +// - [0-9] writes a random byte to addresses [0-9] (either 0xAA or 0xBB) +// Get the SPIFlash library from here: https://github.com/LowPowerLab/SPIFlash +// ********************************************************************************** +// Copyright Felix Rusu, LowPowerLab.com +// Library and code by Felix Rusu - felix@lowpowerlab.com +// ********************************************************************************** +// License +// ********************************************************************************** +// 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. +// +// You should have received a copy of the GNU General +// Public License along with this program. +// If not, see . +// +// Licence can be viewed at +// http://www.gnu.org/licenses/gpl-3.0.txt +// +// Please maintain this license information along with authorship +// and copyright notices in any redistribution of this code +// ********************************************************************************** + + +#include //get it here: https://github.com/LowPowerLab/SPIFlash +#include + +#define SERIAL_BAUD 115200 +char input = 0; +long lastPeriod = -1; + +#ifdef __AVR_ATmega1284P__ + #define LED 15 // Moteino MEGAs have LEDs on D15 + #define FLASH_SS 23 // and FLASH SS on D23 +#else + #define LED 9 // Moteinos have LEDs on D9 + #define FLASH_SS 8 // and FLASH SS on D8 +#endif + +////////////////////////////////////////// +// flash(SPI_CS, MANUFACTURER_ID) +// SPI_CS - CS pin attached to SPI flash chip (8 in case of Moteino) +// MANUFACTURER_ID - OPTIONAL, 0x1F44 for adesto(ex atmel) 4mbit flash +// 0xEF30 for windbond 4mbit flash +////////////////////////////////////////// +SPIFlash flash(FLASH_SS, 0xEF30); + +void setup(){ + Serial.begin(SERIAL_BAUD); + Serial.print("Start..."); + + if (flash.initialize()) + Serial.println("Init OK!"); + else + Serial.println("Init FAIL!"); +} + +void loop(){ + // Handle serial input (to allow basic DEBUGGING of FLASH chip) + // ie: display first 256 bytes in FLASH, erase chip, write bytes at first 10 positions, etc + if (Serial.available() > 0) { + input = Serial.read(); + if (input == 'd') //d=dump flash area + { + Serial.println("Flash content:"); + int counter = 0; + + while(counter<=256){ + Serial.print(flash.readByte(counter++), HEX); + Serial.print('.'); + } + + Serial.println(); + } + else if (input == 'e') + { + Serial.print("Erasing Flash chip ... "); + flash.chipErase(); + while(flash.busy()); + Serial.println("DONE"); + } + else if (input == 'i') + { + Serial.print("DeviceID: "); + Serial.println(flash.readDeviceId(), HEX); + } + else if (input >= 48 && input <= 57) //0-9 + { + Serial.print("\nWriteByte("); Serial.print(input); Serial.print(")"); + flash.writeByte(input-48, millis()%2 ? 0xaa : 0xbb); + } + } + + // Periodically blink the onboard LED while listening for serial commands + if ((int)(millis()/500) > lastPeriod) + { + lastPeriod++; + pinMode(9, OUTPUT); + digitalWrite(9, lastPeriod%2); + } +} \ No newline at end of file diff --git a/libraries/SPIFlash/keywords.txt b/libraries/SPIFlash/keywords.txt new file mode 100644 index 0000000..652da98 --- /dev/null +++ b/libraries/SPIFlash/keywords.txt @@ -0,0 +1,18 @@ +SPIFlash KEYWORD1 +initialize KEYWORD2 +command KEYWORD2 +readStatus KEYWORD2 +readByte KEYWORD2 +readBytes KEYWORD2 +writeByte KEYWORD2 +writeBytes KEYWORD2 +flashBusy KEYWORD2 +chipErase KEYWORD2 +blockErase4K KEYWORD2 +blockErase32K KEYWORD2 +readDeviceId KEYWORD2 +readUniqueId KEYWORD2 +UNIQUEID KEYWORD2 +sleep KEYWORD2 +wakeup KEYWORD2 +end KEYWORD2 \ No newline at end of file diff --git a/libraries/SPIFlash/library.json b/libraries/SPIFlash/library.json new file mode 100644 index 0000000..b199318 --- /dev/null +++ b/libraries/SPIFlash/library.json @@ -0,0 +1,12 @@ +{ + "name": "SPIFlash", + "keywords": "flash, spi", + "description": "Library for read/write access to SPI flash memory chips", + "repository": + { + "type": "git", + "url": "https://github.com/LowPowerLab/SPIFlash.git" + }, + "frameworks": "arduino", + "platforms": "atmelavr" +} From c3e30662afa0d227acf3ad26686bfbfff467a8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=B8rch?= Date: Thu, 5 Feb 2015 19:17:44 +0100 Subject: [PATCH 2/9] Added default mysensorMicro sketch --- .../examples/MysensorMicro/MysensorMicro.ino | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 libraries/MySensors/examples/MysensorMicro/MysensorMicro.ino diff --git a/libraries/MySensors/examples/MysensorMicro/MysensorMicro.ino b/libraries/MySensors/examples/MysensorMicro/MysensorMicro.ino new file mode 100644 index 0000000..11bf608 --- /dev/null +++ b/libraries/MySensors/examples/MysensorMicro/MysensorMicro.ino @@ -0,0 +1,191 @@ +// Default sensor sketch for MySensor Micro module +// Act as a temperature / humidity sensor by default. +// +// If A1 is held low while powering on, it will clear eeprom (and thereby the stored nodeid) +// +// Battery voltage is repported as child sensorId 199, as well as battery percentage + + +#include +#include +#include +#include +#include + +// Define a static node address, remove if you want auto address assignment +//#define NODE_ADDRESS 3 + +// Child sensor ID's +#define CHILD_ID_TEMP 1 +#define CHILD_ID_HUM 2 +#define CHILD_ID_BATT 199 + +//Pin definitions +#define RESET_CFG_PIN A1 +#define LED_PIN A2 + +#define MEASURE_INTERVAL 60000 + +SI7021 humiditySensor; +MySensor gw; + +// Sensor messages +MyMessage msgHum(CHILD_ID_HUM, V_HUM); +MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP); +MyMessage msgBattery(CHILD_ID_BATT, V_VOLTAGE); + +// Global settings +int measureCount = 0; + +// Storage of old measurements +float lastTemperature = -100; +int lastHumidity = -100; +long lastBattery = -100; + + +void setup() { + + Serial.begin(115200); + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, LOW); + + // First check if we should boot into clear eeprom mode + pinMode(RESET_CFG_PIN, INPUT); + digitalWrite(RESET_CFG_PIN, HIGH); // Enable pullup + if (!digitalRead(RESET_CFG_PIN)) resetEEP(); + digitalWrite(RESET_CFG_PIN, LOW); + + digitalWrite(LED_PIN, HIGH); + +#ifdef NODE_ADDRESS + gw.begin(NULL, NODE_ADDRESS, false); +#else + gw.begin(NULL,AUTO,false); +#endif + + digitalWrite(LED_PIN, LOW); + + humiditySensor.begin(); + + gw.sendSketchInfo("EnvironmentSensor", "1.0"); + + gw.present(CHILD_ID_TEMP,S_TEMP); + gw.present(CHILD_ID_HUM,S_HUM); + + gw.present(CHILD_ID_BATT, S_POWER); +} + + +// Main loop function +void loop() { + measureCount ++; + bool forceTransmit = false; + + if (measureCount > 30) {// Every 60th time we wake up, force a transmission on all sensors. + forceTransmit = true; + measureCount = 0; + } + + gw.process(); + sendBattLevel(forceTransmit); + sendTempHumidityMeasurements(forceTransmit); + + gw.sleep(MEASURE_INTERVAL); +} + +/* + * Sends temperature and humidity from Si7021 sensor + * + * Parameters + * - force : Forces transmission of a value (even if it's the same as previous measurement) + */ +void sendTempHumidityMeasurements(bool force) +{ + if (force) { + lastHumidity = -100; + lastTemperature = -100; + } + + float temperature = humiditySensor.getCelsiusHundredths()/10; + + temperature = temperature / 10; + + int humidity = humiditySensor.getHumidityPercent(); + + if (lastTemperature != temperature) { + gw.send(msgTemp.set(temperature,1)); + lastTemperature = temperature; + Serial.print("temperature "); + Serial.println(temperature); + } + if (lastHumidity != humidity) { + gw.send(msgHum.set(humidity)); + lastHumidity = humidity; + Serial.print("Humidity "); + Serial.println(humidity); + } +} + +/* + * Sends battery information (both voltage, and battery percentage) + * + * Parameters + * - force : Forces transmission of a value + */ +void sendBattLevel(bool force) +{ + if (force) lastBattery = -1; + long vcc = readVcc(); + if (vcc != lastBattery) { + lastBattery = vcc; + // Calculate percentage + gw.send(msgBattery.set(vcc)); + vcc = vcc - 1900; // subtract 1.9V from vcc, as this is the lowest voltage we will operate at + + long percent = vcc / 14; + gw.sendBatteryLevel(percent); + } +} + +long readVcc() { + // Read 1.1V reference against AVcc + // set the reference to Vcc and the measurement to the internal 1.1V reference + #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); + #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) + ADMUX = _BV(MUX5) | _BV(MUX0); + #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + ADMUX = _BV(MUX3) | _BV(MUX2); + #else + ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); + #endif + + delay(2); // Wait for Vref to settle + ADCSRA |= _BV(ADSC); // Start conversion + while (bit_is_set(ADCSRA,ADSC)); // measuring + + uint8_t low = ADCL; // must read ADCL first - it then locks ADCH + uint8_t high = ADCH; // unlocks both + + long result = (high<<8) | low; + + result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 + return result; // Vcc in millivolts +} + + +/* + * Resets eeprom to default value (while blinking LED_PIN) + */ +void resetEEP() +{ + for (int i=0;i<512;i++) { + digitalWrite(LED_PIN, HIGH); + delay(100); + digitalWrite(LED_PIN, LOW); + delay(100); + EEPROM.write(i, 0xff); + } +} + + From bd80220cf3c54d26680f1a25b202f9d57e7b8391 Mon Sep 17 00:00:00 2001 From: tekka Date: Fri, 6 Feb 2015 14:07:40 +0100 Subject: [PATCH 3/9] MYSBootloader 1.0 Bootloader for (direct) OTA FW updates. Occupies 1834 bytes .hex is compiled for 16Mhz. Bootloader was tested in this frequency range; 128kHz (intRC) to 16Mhz(XTAL) at 3.3 and 5V. For OTA bootloading, MYSController was used, NodeJS Controller not tested. --- MYSBootloader/MYSBootloader.c | 307 ++++++++++++++++++++++++++++++ MYSBootloader/MYSBootloader.h | 57 ++++++ MYSBootloader/MYSBootloader.hex | 117 ++++++++++++ MYSBootloader/MYSBootloaderHW.h | 147 ++++++++++++++ MYSBootloader/MYSBootloaderRF24.h | 210 ++++++++++++++++++++ MYSBootloader/Makefile | 65 +++++++ MYSBootloader/boards.txt | 62 ++++++ 7 files changed, 965 insertions(+) create mode 100644 MYSBootloader/MYSBootloader.c create mode 100644 MYSBootloader/MYSBootloader.h create mode 100644 MYSBootloader/MYSBootloader.hex create mode 100644 MYSBootloader/MYSBootloaderHW.h create mode 100644 MYSBootloader/MYSBootloaderRF24.h create mode 100644 MYSBootloader/Makefile create mode 100644 MYSBootloader/boards.txt diff --git a/MYSBootloader/MYSBootloader.c b/MYSBootloader/MYSBootloader.c new file mode 100644 index 0000000..462d99c --- /dev/null +++ b/MYSBootloader/MYSBootloader.c @@ -0,0 +1,307 @@ +/* + MYSBootloader: OTA bootloader for Mysensor nodes (www.mysensors.org) + Code based on OTA bootloader by ToSa + Optimized and extended by tekka + Version 1.0 / 20150206 + Size: 1834 bytes + + Tested with Atmega328P, 16Mhz 3.3V and 5V and MYSController 0.1.2.266 (goo.gl/9DCWNo) + + MCU: atmega328 + clock: 16Mhz (prescaler: off/1) + bootsz: 1024W + + + fuses for ISP: + EX = 0xFE (use 0x06 for Arduino IDE, boards.txt) + HI = 0xDA + LO = 0xF7 + + nRF24L01+ connected to pins: + CE = 9 + CSN = 10 + + Successfully tested with: + + 16Mhz extXTAL, 3.3V + 1Mhz intRC (8Mhz + DIV8), 3.3V + 128kHz intRC, 3.3V + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ + +#include "MYSBootloader.h" + + +#define MYSBOOTLOADER_MAJVER 1 +#define MYSBOOTLOADER_MINVER 0 + +#define MYSBOOTLOADER_VERSION (MYSBOOTLOADER_MINVER * 256 + MYSBOOTLOADER_MAJVER) + +//#define USE_PRESCALER +#define MAX_RESEND 5 + + +// procedures and functions + +int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9"))); +static uint16_t calcCRCrom (const void* ptr, uint16_t len); +static uint8_t IsFirmwareValid(); +static void reboot(); +static void startup(); +static void programPage(uint32_t page, uint8_t *buf); +static boolean sendWrite(MyMessage message); +static bool sendAndWait(uint8_t reqType, uint8_t resType); + + +static uint16_t calcCRCrom (const void* ptr, uint16_t len) { + // init 0xFFFF + uint16_t crc = ~0; + for (uint16_t i = 0; i < len; ++i) { + crc = _crc16_update(crc, pgm_read_byte((uint16_t) ptr + i)); + } + return crc; +} + +static uint8_t IsFirmwareValid () { + return calcCRCrom(0, fc.blocks * FIRMWARE_BLOCK_SIZE) == fc.crc; +} + +static void reboot() { + // any pending eeprom activities? + eeprom_busy_wait(); + // trigger watchdog ASAP + watchdogConfig(WATCHDOG_16MS); + // wait until WD triggers + while (1); +} + +static void startup() { + if (IsFirmwareValid()) { + // WD off + watchdogConfig(WATCHDOG_OFF); + + #ifdef USE_PRESCALER + // reset prescaler + clock_prescale_set(orgClockDiv); + #endif + + // start sketch + ((void(*)()) 0)(); + + } else { + reboot(); + } +} + +static void programPage(uint32_t page, uint8_t *buf) { + // using out commands, saves some bytes :) + __boot_page_erase_short(page); + boot_spm_busy_wait(); + for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) { + uint16_t w = *buf++; + // generate word + w += (*buf++) << 8; + __boot_page_fill_short(page + i, w); + } + __boot_page_write_short(page); + boot_spm_busy_wait(); + __boot_rww_enable_short(); +} + + +static boolean sendWrite(MyMessage message) { + return write(nc.parentNodeId, message.array, HEADER_SIZE + mGetLength(message), (message.destination == BROADCAST_ADDRESS)); +} + +static bool sendAndWait(uint8_t reqType, uint8_t resType) { + outMsg.type = reqType; + // outer loop, retries + for (uint8_t i = 0; i < MAX_RESEND; i++) { + sendWrite(outMsg); + // loop 20 times, wait time 0.1ms if no/wrong data => 2s + for (uint8_t j = 0; j < 20; j++) { + // loop 100 times, wait 1ms if no/wrong data => 0.1s + for (uint8_t k = 0; k < 100; k++) { + watchdogReset(); + // Tx FIFO data available? (we don't care about the pipe here) + if (available(NULL)) { + // read message from FIFO + readMessage(inMsg.array); + // protocol compatible? if not ignore msg + if ((mGetVersion(inMsg) != PROTOCOL_VERSION)) { + continue; + } + if (inMsg.destination == nc.nodeId) { + // msg for us + if ((mGetCommand(inMsg) == C_INTERNAL) && (inMsg.type == I_FIND_PARENT_RESPONSE)) { + if (inMsg.bValue < nc.distance - 1) { + // got new routing info, update settings + nc.distance = inMsg.bValue + 1; + nc.parentNodeId = inMsg.sender; + } + } + // did we receive expected reply? + if ((mGetCommand(inMsg) == mGetCommand(outMsg)) && (inMsg.type == resType)) { + return true; + } + + } + } else { + // wait 1ms if no data available + delaym(10); + } + } + } + } + return false; +} + + + + +// main start +int main(void) { + + asm volatile ("clr __zero_reg__"); + // reset MCU status register + MCUSR = 0; + + #ifdef USE_PRESCALER + // get current prescale + orgClockDiv = clock_prescale_get(); + //switch to 4 MHz on 16Mhz crystal + clock_prescale_set(F_CPU_DIV); + #endif + + // enable watchdog to avoid deadlock + watchdogConfig(WATCHDOG_8S); + + // initialize SPI + SPIinit(); + + // initialize RF module + RFinit(); + + // Read node config from EEPROM, i.e. nodeId, parent nodeId, distance + eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(struct NodeConfig)); + // Read firmware config from EEPROM, i.e. type, version, CRC, blocks + eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); + + // bootloader should find closest node during each reboot + + // invalidate parent node settings, since we have to re-discover them for every single reboot + nc.distance = 0xFF; + nc.parentNodeId = 0xFF; + + // prepare for I_FIND_PARENTS + outMsg.sender = nc.nodeId; + outMsg.last = nc.nodeId; + outMsg.sensor = 0xFF; + outMsg.destination = BROADCAST_ADDRESS; + + mSetVersion(outMsg, PROTOCOL_VERSION); + mSetLength(outMsg, 0); + mSetCommand(outMsg, C_INTERNAL); + mSetAck(outMsg,false); + mSetPayloadType(outMsg, P_STRING); + + // set reading & writing pipe address + setAddress(nc.nodeId); + + // network up?, get neighbors, else startup + if (!sendAndWait(I_FIND_PARENT, I_FIND_PARENT_RESPONSE)) { + startup(); + } + + // all messages to gateway + outMsg.destination = GATEWAY_ADDRESS; + + // if no node id assigned, request new id + if (nc.nodeId == AUTO) { + // listen to broadcast + openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(BROADCAST_ADDRESS)); + if (sendAndWait(I_ID_REQUEST, I_ID_RESPONSE)) { + // save id to eeprom + eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, atoi(inMsg.data)); + } + // we could go on and set everything right here, but rebooting will take care of that - saves bytes :) + reboot(); + } + + // wuff + watchdogReset(); + // prepare for FW config request + RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)outMsg.data; + mSetLength(outMsg, sizeof(RequestFirmwareConfig)); + mSetCommand(outMsg, C_STREAM); + mSetPayloadType(outMsg,P_CUSTOM); + // copy node settings to reqFWConfig + memcpy(reqFWConfig,&fc,sizeof(NodeFirmwareConfig)); + // add bootloader information + reqFWConfig->BLVersion = MYSBOOTLOADER_VERSION; + + // send node config and request FW config from controller + if (!sendAndWait(ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE)) { + startup(); + } + + NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)inMsg.data; + + // compare with current node configuration, if equal startup + if (!memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { + startup(); + } + + // *********** from here on we will fetch new FW + + // invalidate current CRC + fc.crc = 0xFFFF; + // write fetched type and version in case OTA fails (BL will reboot and re-request FW with stored settings) + eeprom_write_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig)); + + // copy new FW config + memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); + RequestFWBlock *firmwareRequest = (RequestFWBlock *)outMsg.data; + mSetLength(outMsg, sizeof(RequestFWBlock)); + + firmwareRequest->type = fc.type; + firmwareRequest->version = fc.version; + + // request FW from controller + for (uint16_t block = fc.blocks; block > 0; block--) { + firmwareRequest->block = (block - 1); + // request FW block + if (!sendAndWait(ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE)) { + reboot(); + } + + ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)inMsg.data; + // calculate page offset + uint8_t offset = ((block - 1) * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE; + // write to buffer + memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); + // program if page full + if (offset == 0) { + programPage(((block - 1) * FIRMWARE_BLOCK_SIZE), progBuf); + } + } + + // wuff + watchdogReset(); + + // all blocks transmitted, calc CRC and write to eeprom if valid + if (IsFirmwareValid()) { + // if FW is valid, write settings to eeprom + eeprom_write_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); + // we could restart directly here, but eeprom may still be busy, thus reboot (we check for idle eeprom before rebooting) and save some bytes :) + } + // final step + reboot(); +} + + + + diff --git a/MYSBootloader/MYSBootloader.h b/MYSBootloader/MYSBootloader.h new file mode 100644 index 0000000..10c48d8 --- /dev/null +++ b/MYSBootloader/MYSBootloader.h @@ -0,0 +1,57 @@ +#ifndef MyOtaBootloader_H +#define MyOtaBootloader_H + +#include +#include +#include +#include + +#include "MyMessage.h" +#include "MySensor.h" +#include "boot.h" +#include "MYSBootloaderRF24.h" + + +#define FIRMWARE_BLOCK_SIZE 16 + +// FW config structure, stored in eeprom +typedef struct { + uint16_t type; + uint16_t version; + uint16_t blocks; + uint16_t crc; +} __attribute__((packed)) NodeFirmwareConfig; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t blocks; + uint16_t crc; + uint16_t BLVersion; +} __attribute__((packed)) RequestFirmwareConfig; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t block; +} __attribute__((packed)) RequestFWBlock; + +typedef struct { + uint16_t type; + uint16_t version; + uint16_t block; + uint8_t data[FIRMWARE_BLOCK_SIZE]; +} __attribute__((packed)) ReplyFWBlock; + +static struct NodeConfig nc; +static NodeFirmwareConfig fc; +static MyMessage outMsg; +static MyMessage inMsg; + +#ifdef PRESCALE +static clock_div_t orgClockDiv = 0; +#endif + +static uint8_t progBuf[SPM_PAGESIZE]; + +#endif // MyOtaBootloader_H \ No newline at end of file diff --git a/MYSBootloader/MYSBootloader.hex b/MYSBootloader/MYSBootloader.hex new file mode 100644 index 0000000..7039c3b --- /dev/null +++ b/MYSBootloader/MYSBootloader.hex @@ -0,0 +1,117 @@ +:1078000021E0A0E0B1E001C01D92A53DB207E1F783 +:10781000CDB7DEB76897DEBFCDBF112414BE88E1B7 +:107820008093600089E2809360008FEE84B963E00A +:1078300083E201D26FE584E2FED16BE381E2FBD10A +:107840006CE485E2F8D167E286E2F5D10DD263E718 +:1078500080E5F1D166E08DE3EED16FE38CE3EBD10F +:107860006EEF80E2E8D185E053D143E050E060E084 +:1078700070E08AEC91E032D348E050E06BE171E0D7 +:1078800082EC91E02BD38FEF8093CC018093CB01DE +:107890002091CA012093A2012093A1018093A70106 +:1078A0008093A30182E08093A4018091A501887058 +:1078B00083608093A5012F3FD1F130E040E050E09C +:1078C00060E070E080E090E034504E4157456745FD +:1078D0007F4F8F4F9F4F29013A014B015C01298B4C +:1078E0005A8A4B8B7C8A6D8B9E8A8F8BB88E85E0F3 +:1078F000FE017196ADECB1E001900D928A95E1F731 +:1079000045E0BE016F5E7F4F8AE27FD149865A868D +:107910006B867C868D869E86AF86B88A45E0BE01E2 +:10792000675F7F4F8BE271D163E082E284D168E0D0 +:1079300087E09FD1811101C049D11092A3018091AC +:10794000CA018F3FF1F489838CEF8A8381EE8B83A8 +:1079500088EA8C838D831E821F82188645E0BE01D3 +:107960006F5F7F4F8BE251D164E083E082D1882347 +:1079700039F087E891E080D2682F80E090E0C7D2AC +:107980001CD1A8958091A401877080658093A40183 +:107990008091A5018871846C8093A50188E0E2EC58 +:1079A000F1E0A8EAB1E001900D928A95E1F781E05B +:1079B00090E09093B1018093B00161E080E059D1F3 +:1079C000811101C003D148E050E067E871E082EC2A +:1079D00091E06FD2892B09F4F9D08FEF9FEF90934C +:1079E000C9018093C80148E050E06BE171E082EC8E +:1079F00091E084D288E0E7E8F1E0A2ECB1E0019008 +:107A00000D928A95E1F78091A4018770806380933D +:107A1000A4018091C2019091C3019093A901809328 +:107A2000A8018091C4019091C5019093AB0180930E +:107A3000AA01A090C601B090C70185010150104F66 +:107A400064E0000F111F6A95E1F770E1972E88241A +:107A50008394E5E07E2EF1E16F2EA114B10409F4C8 +:107A60004AC081E0A81AB108B092AD01A092AC0161 +:107A700063E082E0FED0882309F482CF802F8077F4 +:107A8000A82FB0E0A050BF4FEDE8F1E0992D019094 +:107A90000D929A95E1F781112BC083E0F80187BF21 +:107AA000E89507B600FCFDCF4091000180E090E032 +:107AB000DC01A050BF4FFC01E00FF11F11962C918B +:107AC000119730E0322F2227240F311D090187BE84 +:107AD000E895112412964C9102968038910541F751 +:107AE000F80177BEE89507B600FCFDCF67BEE895C4 +:107AF00000511109B2CFA8952CD0882309F440CFAA +:107B000048E050E06BE171E082EC91E0F7D138CFD2 +:107B100090E0981751F02DE730E000002150310936 +:107B200021153105D1F79F5FF4CF089598E087FFC5 +:107B300002C02B9A01C02B9800002D9A000023B19F +:107B4000880F22952170822B2D980000915079F793 +:107B500008958091C6019091C70124E0880F991F74 +:107B60002A95E1F7E0E0F0E02FEF3FEFE817F907A3 +:107B7000D1F044912427422F42954227042E469566 +:107B800046954025042E469540254770022E232F0A +:107B9000469507944795302D242706944795302520 +:107BA00024273196E3CF81E04091C8015091C9016B +:107BB0002417350709F080E00895F999FECF88E190 +:107BC0008093600088E080936000FFCFC2DF88234D +:107BD00041F088E18093600010926000E0E0F0E006 +:107BE0000994EBDF1F93CF93DF93EB01142F2A98B7 +:107BF0009DDF1C0F1C1721F08FEF98DF8993FACFC0 +:107C00002A9ADF91CF911F9108951F93CF93DF930D +:107C1000EB01142F2A988ADF1C0F1C1719F0899189 +:107C200085DFFBCF2A9ADF91CF911F91089541E024 +:107C300060E070E0D7CFCF93DF931F92CDB7DEB770 +:107C4000698341E0BE016F5F7F4FDFDF0F90DF91FF +:107C5000CF91089540E060E070E082EED6CF40E042 +:107C600060E070E081EED1CFF5DFF9DF60E787E219 +:107C7000E2CF6F927F928F929F92AF92BF92CF92FC +:107C8000DF92EF92FF920F931F93CF93DF93CDB7C5 +:107C9000DEB7AA97DEBFCDBF762E8093A60105E0A2 +:107CA00041E2642EE1EAF1E0DE011196862D0190B9 +:107CB0000D928A95E1F7FB801C8116951695169515 +:107CC000195F2091CB0129982AA7CEDF6EEF80E2C1 +:107CD000B2DF2AA530E040E050E060E070E080E0F4 +:107CE00090E034504E41574567457F4F8F4F9F4F2F +:107CF0002AA33BA34CA35DA36EA37FA388A799A748 +:107D000045E0BE016E5D7F4F8AE27FDF45E0BE0148 +:107D10006E5D7F4F80E379DF8FEFF81202C080EB5A +:107D200001C080EA412FBE016F5F7F4F6EDF299A4D +:107D300081E0EEDE299840E060E070E08FEF52DFF6 +:107D40008073C9F360E787E276DF84FD88DF6FE048 +:107D500080E271DF89DF8090CD019090CE01A0900C +:107D6000CF01B090D001C090D101D090D201E0906D +:107D7000D301F090D4019401A501B601C701A0E0A0 +:107D80006FD029F045E06DEC71E08AE23EDF299A80 +:107D900014E134E6F32EA89587E149DF80FD4BC05E +:107DA00080E645DF813210F055DF80E0482F60E843 +:107DB00071E081E617DF60E787E23DDF80918301B4 +:107DC0008770823059F5909182018091CA01981391 +:107DD00025C040918401842F87708330B1F4809155 +:107DE0008501883091F450918701252F30E08091F2 +:107DF000CC0190E00197281739073CF45F5F50935E +:107E0000CC01809181018093CB018091A5018427D1 +:107E1000877021F480918501871559F0FA9409F053 +:107E2000BACF115009F0B5CF015009F03BCF80E037 +:107E300005C081E003C08AE06BDEF0CFAA96DEBF0A +:107E4000CDBFDF91CF911F910F91FF90EF90DF9009 +:107E5000CF90BF90AF909F908F907F906F9008953C +:107E60000024A7FD00942A17300540055005600541 +:107E70007005800590050895FC0188279927E894EE +:107E800021912032E9F3293010F02E30C8F32B3243 +:107E900039F02D3231F4689403C018D0820F911D4F +:107EA000219120532A30C8F31EF4909581959F4F5D +:107EB0000895FB01DC0104C08D910190801921F42B +:107EC00041505040C8F7881B990B08957AE0979F5E +:107ED000902D879F802D910D11240895DC01CB01F9 +:107EE000FC01F999FECF06C0F2BDE1BDF89A3196CA +:107EF00000B40D9241505040B8F70895DC01CB0119 +:107F000002C02D9105D041505040D8F70895262F3A +:107F1000F999FECF1FBA92BD81BD20BD0FB6F8946E +:0A7F2000FA9AF99A0FBE019608952F +:040000030000780081 +:00000001FF diff --git a/MYSBootloader/MYSBootloaderHW.h b/MYSBootloader/MYSBootloaderHW.h new file mode 100644 index 0000000..8769ce1 --- /dev/null +++ b/MYSBootloader/MYSBootloaderHW.h @@ -0,0 +1,147 @@ +#ifndef MYSBootloaderHW_H +#define MYSBootloaderHW_H + +#include + +// hardware + + + +#ifdef USE_PRESCALER +#define F_CPU_DIV clock_div_4 +#else +#define F_CPU_DIV clock_div_1 +#endif + +#define F_CPU_REAL F_CPU / (1 << F_CPU_DIV) +#define DELAY_M 125 * (1 << F_CPU_DIV) +#define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 ) +#define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1))) +#define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE) +#define UART_SRA UCSR0A +#define UART_SRB UCSR0B +#define UART_SRC UCSR0C +#define UART_SRL UBRR0L +#define UART_UDR UDR0 + +// boolean definition + +typedef uint8_t bool; +typedef uint8_t boolean; +#define false 0x0 +#define true 0x1 +#define LOW 0x0 +#define HIGH 0x1 + +// Watchdog definitions and functions + +#define WATCHDOG_OFF (0) +#define WATCHDOG_16MS (_BV(WDE)) +#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE)) +#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE)) +#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE)) +#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE)) +#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE)) +#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE)) +#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE)) +#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE)) +#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE)) + +inline void watchdogReset() { + asm volatile ("wdr"); +} + +void watchdogConfig(uint8_t x) { + WDTCSR = _BV(WDCE) | _BV(WDE); + WDTCSR = x; +} + +// delay procedures + +inline static void delayu() { + asm volatile ("nop"); +} + +static void delaym(uint8_t value) { + for (uint8_t outer = 0; outer < value; outer++) { + for (uint16_t inner = 0; inner < DELAY_M; inner++) { + delayu(); + } + } +} + +// SPI communication + +#define SPI_DDR DDRB +#define SPI_PORT PORTB +#define SPI_PIN PINB +#define SPI_SCLK 5 // Arduino Pin 13 <-> Bit 5 of port B +#define SPI_MISO 4 // Arduino Pin 12 <-> Bit 4 of port B +#define SPI_MOSI 3 // Arduino Pin 11 <-> Bit 3 of port B +#define SPI_CSN 2 // Arduino Pin 10 <-> Bit 2 of port B +#define SPI_CE 1 // Arduino Pin 9 <-> Bit 1 of port B +#define csnlow() SPI_PORT &= ~_BV(SPI_CSN)//;delaym(1) // not necessary +#define csnhigh() SPI_PORT |= _BV(SPI_CSN)//;delaym(1) // not necessary +#define celow() SPI_PORT &= ~_BV(SPI_CE)//;delaym(1) // not necessary +#define cehigh() SPI_PORT |= _BV(SPI_CE)//;delaym(1) // minimum puls width 10us + +static void SPIinit() { + // set pin mode: MOSI,SCLK,CE = OUTPUT, MISO = INPUT + SPI_DDR = _BV(SPI_MOSI) | _BV(SPI_SCLK) | _BV(SPI_CE) | _BV(SPI_CSN) | ~_BV(SPI_MISO); +} + +static uint8_t SPItransfer(uint8_t value) { + for(uint8_t i = 0 ;i < 8;i++) { + if (value & 0x80) SPI_PORT |= _BV(SPI_MOSI); else SPI_PORT &= ~_BV(SPI_MOSI); + delayu(); + value <<= 1; + SPI_PORT |= _BV(SPI_SCLK); + delayu(); + value |= ((SPI_PIN >> SPI_MISO) & 0x01); + SPI_PORT &= ~_BV(SPI_SCLK); + delayu(); + } + return value; +} + + +// UART debug + +static void uart_init() { + //Double speed mode USART0 + UART_SRA = _BV(U2X0); + UART_SRB = _BV(RXEN0) | _BV(TXEN0); + UART_SRC = _BV(UCSZ00) | _BV(UCSZ01); + UART_SRL = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 ); +} + +void putch(char ch) { + while (!(UART_SRA & _BV(UDRE0))); + UART_UDR = ch; +} + +uint8_t getch() { + uint8_t ch; + // wait until char received + while(!(UART_SRA & _BV(RXC0))); + // framing error? + if (!(UART_SRA & _BV(FE0))) { + watchdogReset(); + } + ch = UART_UDR; + return ch; +} + +static void put_string(char *s) { + while (*s) + putch(*s++); +} + +static void put_int(uint8_t i) { + char s[5]; + itoa( i, s, 10 ); + put_string( s ); +} + + +#endif // MYSBootloaderHW_H diff --git a/MYSBootloader/MYSBootloaderRF24.h b/MYSBootloader/MYSBootloaderRF24.h new file mode 100644 index 0000000..c91f902 --- /dev/null +++ b/MYSBootloader/MYSBootloaderRF24.h @@ -0,0 +1,210 @@ +#ifndef MYSBootloaderRF24_H +#define MYSBootloaderRF24_H + +#include "MYSBootloaderHW.h" +#include "nRF24L01.h" + +typedef enum { RF24_PA_MIN = 0,RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_PA_ERROR } rf24_pa_dbm_e ; +typedef enum { RF24_1MBPS = 0, RF24_2MBPS, RF24_250KBPS } rf24_datarate_e; + +#define _write_register(reg) ( (reg) | W_REGISTER) +#define _read_register(reg) ( (reg) | R_REGISTER) + +#define addr_width 5 +uint64_t pipe0_reading_address = 0; + + +uint8_t BurstReadAddress(uint8_t addr, uint8_t* buf, uint8_t len) { + csnlow(); + uint8_t status = SPItransfer( addr ); + while ( len-- ) { + status = SPItransfer(NOP); + *buf++ = status; + } + csnhigh(); + return status; +} + +uint8_t BurstWriteAddress(uint8_t addr, uint8_t* buf, uint8_t len) { + csnlow(); + uint8_t status = SPItransfer( addr ); + while ( len-- ) { + status = SPItransfer(*buf++); + } + csnhigh(); + return status; +} + +uint8_t ReadAddress(uint8_t addr) { + return BurstReadAddress(addr,NULL,1); +} + + +uint8_t WriteAddress(uint8_t addr, uint8_t val) { + return BurstWriteAddress(addr,&val,1); +} + +#define BurstWriteRegister(reg,buf,len) BurstWriteAddress(_write_register(reg),buf,len) +#define WriteRegister(reg,val) WriteAddress(_write_register(reg),val) +#define BurstReadRegister(reg,buf,len) BurstReadAddress(_read_register(reg),buf,len) +#define ReadRegister(reg) ReadAddress(_read_register(reg)) +#define get_status() BurstReadAddress(NOP,NULL,0) + +void Flush_RX(void) { + BurstWriteAddress(FLUSH_RX,NULL,0); +} + +void Flush_TX(void) { + BurstWriteAddress(FLUSH_TX,NULL,0); +} + +void Flush_RXTX_CLI(void) { + // flush RX and TX buffer + Flush_RX(); + Flush_TX(); + // clear registers + WriteRegister(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); +} + +static void startListening(void) { + // for size reasons, we do not read back the register content, set PRIM_RX + WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | _BV(PRIM_RX)) ; + Flush_RXTX_CLI(); + // write pipe0 address + if (pipe0_reading_address){ + BurstWriteRegister(RX_ADDR_P0, (uint8_t*)&pipe0_reading_address, addr_width); + } + cehigh(); +} + +static void stopListening(void) { + celow(); + Flush_RXTX_CLI(); + // for size reasons, we do not read back the register content, clear PRIM_RX + WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | ~_BV(PRIM_RX)) ; +} + + + +static bool write_buf(uint8_t* buf, uint8_t len, const bool multicast ) { + // write payload to FIFO + BurstWriteRegister( multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD, buf, len) ; + // CE pulse to start transmission + cehigh(); + // IMPORTANT: minimum CE pulse width 10us, see nRF24L01 specs + delaym(1); + // start transmitting + celow(); + + + // wait until sent, here we potentially have a deadlock when transmitter not connected/not working + while( ! ( get_status() & ( _BV(TX_DS) | _BV(MAX_RT) ))) { } + // read interrupts and clear + uint8_t status = WriteRegister(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) ); + // max retries? clear FIFO + if( status & _BV(MAX_RT)){ + Flush_TX(); + return false; + } + return true; +} + +static uint8_t getDynamicPayloadSize(void) { + uint8_t result = ReadAddress(R_RX_PL_WID); + if(result > 32) { + Flush_RX(); + result = 0; + } + return result; +} + + +static bool available(uint8_t* pipe_num) { + if (!(ReadAddress(FIFO_STATUS) & _BV(RX_EMPTY) )){ + if (pipe_num) { + uint8_t status = get_status(); + *pipe_num = ( status >> RX_P_NO ) & 0b111; + } + return true; + } + return false; +} + + +static void readMessage(uint8_t* buf) { + // read payload + BurstReadRegister(R_RX_PAYLOAD,buf,getDynamicPayloadSize()); + // reset interrupts + WriteRegister(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS)); +} + +static void openWritingPipe(uint64_t value) { + BurstWriteRegister(RX_ADDR_P0, (uint8_t*)&value, addr_width); + BurstWriteRegister(TX_ADDR, (uint8_t*)&value, addr_width); +} + +static void openReadingPipe(uint8_t pipe, uint64_t address) { + // If this is pipe 0, cache the address. This is needed because openWritingPipe() will overwrite the pipe 0 address, so + // startListening() will have to restore it + if (pipe == WRITE_PIPE) { + memcpy(&pipe0_reading_address,&address,addr_width); + } + // only write full address for pipe 0 and 1 + // since we do not use the BROADCASTING PIPE in this bootloader, we can remove the check + //if ( pipe < 2 ) + { + BurstWriteRegister(RX_ADDR_P0 + pipe, (uint8_t*)&address, addr_width); + } + + +} + +static boolean write(uint8_t destination, uint8_t* buf, uint8_t len, boolean multicast) { + stopListening(); + openWritingPipe(TO_ADDR(destination)); + bool result = write_buf(buf, len, multicast); + startListening(); + return result; +} + +static void setAddress(uint8_t addr) +{ + if (addr != BROADCAST_ADDRESS) { + openReadingPipe(WRITE_PIPE, TO_ADDR(addr)); + openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(addr)); + // enable pipe + WriteRegister(EN_RXADDR,_BV(ERX_P0 + WRITE_PIPE) | _BV(ERX_P0 + CURRENT_NODE_PIPE)); + } +} + +static void RFinit(void){ + // set address width + WriteRegister(SETUP_AW, (addr_width-2) % 4); + // auto retransmit delay 1500us, auto retransmit count 15 + WriteRegister(SETUP_RETR, 5 << ARD | 15 << ARC); + // enable auto ack on all pipes, except broadcasting pipe + WriteRegister(EN_AA, 0b00111111 & ~_BV(BROADCAST_PIPE)) ; + // set channel + WriteRegister(RF_CH, RF24_CHANNEL); + // set data rate + WriteRegister(RF_SETUP, ((RF24_DATARATE & 0b00000010 ) << 4) | ((RF24_DATARATE & 0b00000001 ) << 3) | ((RF24_PA_LEVEL << 1) + 1)); + // flush RX and TX FIFO, clear interrupts + Flush_RXTX_CLI(); + // activate dynamic payload feature + WriteAddress(ACTIVATE,0x73); + // enable payload with ACK and dynamic payload length + WriteRegister(FEATURE, _BV(EN_ACK_PAY) | _BV(EN_DPL) ); + // Enable dynamic payload length on all pipes + WriteRegister(DYNPD, _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0)); + //powerUp, enable 16bit CRC, no RX mode + WriteRegister(CONFIG, _BV(PWR_UP) | _BV(CRCO) | _BV(EN_CRC) | ~_BV(PRIM_RX)); + // stabilize + delaym(5); + + // We are not listening to broadcast pipe since we do not reply to broadcasting messages (e.g. FIND_PARENT_RESPONSE messages) + // openReadingPipe(BROADCAST_PIPE, TO_ADDR(BROADCAST_ADDRESS)); + + +} + +#endif // MYSBootloaderRF24_H diff --git a/MYSBootloader/Makefile b/MYSBootloader/Makefile new file mode 100644 index 0000000..faabf2a --- /dev/null +++ b/MYSBootloader/Makefile @@ -0,0 +1,65 @@ +PROJECT = MYSBootloader +INCLUDES = ../libraries/MySensors + +MCU = atmega328p +CLK = 16000000L + +ISP_PORT = com5 +ISP_SPEED = 115200 +ISP_PROTOCOL = stk500v2 +ISP_MCU = m328p +ISP_HFUSE = DA +ISP_LFUSE = F7 +ISP_EFUSE = 06 +ISP_ARGS = -c$(ISP_PROTOCOL) -P$(ISP_PORT) -b$(ISP_SPEED) -p$(ISP_MCU) + +ifeq ($(OS),Windows_NT) + BINPATH = C:/Program Files (x86)/Arduino/hardware/tools/avr/bin/ + JAVAPATH = C:/Program Files/Java/jdk1.7.0_40/bin/ + JAVAC = $(JAVAPATH)javac.exe + JAVA = $(JAVAPATH)java.exe +else + UNAME_S := $(shell uname -s) + UNAME_P := $(shell uname -p) + UNAME_M := $(shell uname -m) + ifeq ($(UNAME_S),Linux) + ifeq ($(UNAME_P),x86_64) + BINPATH = ../arduino/bin-linux64/bin/ + else ifneq ($(filter %86,$(UNAME_P)),) + BINPATH = ../arduino/bin-linux32/bin/ + else ifneq ($(filter arm%,$(UNAME_M)),) + BINPATH = /usr/bin/ + endif + endif +endif + +CFLAGS = -x c -mno-interrupts -funsigned-char -funsigned-bitfields -DF_CPU=$(CLK)-Os -fno-inline-small-functions -fno-split-wide-types -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -mrelax -Wall -mmcu=$(MCU) -c -std=gnu99 -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" +LDFLAGS = -nostartfiles -Wl,-s -Wl,-static -Wl,-Map="$(OutputFileName).map" -Wl,--start-group -Wl,--end-group -Wl,--gc-sections -mrelax -Wl,-section-start=.text=0x7800 -mmcu=$(MCU) + + + +all: clean out + +clean: + - rm *.o + - rm *.elf + - rm *.hex + +$(PROJECT).o: $(PROJECT).c + "$(BINPATH)avr-gcc" $(CFLAGS) -I$(INCLUDES) $< -o $@ + +$(PROJECT).elf: $(PROJECT).o + "$(BINPATH)avr-gcc" $(LDFLAGS) -o $@ $< -lm + +$(PROJECT).hex: $(PROJECT).elf + "$(BINPATH)avr-objcopy" -O ihex -R .eeprom $< $@ + +out: $(PROJECT).hex + "$(BINPATH)avr-size" $(PROJECT).elf + +load: clean out isp + +isp: $(PROJECT).hex + "$(BINPATH)avrdude" $(ISP_ARGS) -e -u -Ulock:w:0x3f:m -qq -Uefuse:w:0x$(ISP_EFUSE):m -Uhfuse:w:0x$(ISP_HFUSE):m -Ulfuse:w:0x$(ISP_LFUSE):m -Ulock:w:0x0f:m + "$(BINPATH)avrdude" $(ISP_ARGS) -V -q -s -Uflash:w:$(PROJECT).hex + "$(BINPATH)avrdude" $(ISP_ARGS) diff --git a/MYSBootloader/boards.txt b/MYSBootloader/boards.txt new file mode 100644 index 0000000..e84e670 --- /dev/null +++ b/MYSBootloader/boards.txt @@ -0,0 +1,62 @@ +# See: http://code.google.com/p/arduino/wiki/Platforms + +menu.cpu=Processor + +uno.name=Arduino Uno Optiboot + +uno.vid.0=0x2341 +uno.pid.0=0x0043 +uno.vid.1=0x2341 +uno.pid.1=0x0001 + +uno.upload.tool=avrdude +uno.upload.protocol=arduino +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 +uno.upload.speed=115200 + +uno.bootloader.tool=avrdude +uno.bootloader.low_fuses=0xFF +uno.bootloader.high_fuses=0xDE +uno.bootloader.extended_fuses=0x05 +uno.bootloader.unlock_bits=0x3F +uno.bootloader.lock_bits=0x0F +uno.bootloader.file=optiboot/optiboot_atmega328.hex + +uno.build.mcu=atmega328p +uno.build.f_cpu=16000000L +uno.build.board=AVR_UNO +uno.build.core=arduino +uno.build.variant=standard + + +## Arduino Pro or Pro Mini (3V3 & 5V, 16 MHz) w/ ATmega328 MYSBootloader +## ------------------------------------------------- + + +proMYSBL.name=ATmega328 16Mhz MYSBootloader + +proMYSBL.upload.tool=avrdude +proMYSBL.upload.protocol=arduino +proMYSBL.upload.maximum_size=30720 +proMYSBL.upload.maximum_data_size=2048 +proMYSBL.upload.speed=115200 + +proMYSBL.bootloader.tool=avrdude +proMYSBL.bootloader.low_fuses=0xF7 +proMYSBL.bootloader.high_fuses=0xDA +proMYSBL.bootloader.extended_fuses=0x06 +proMYSBL.bootloader.unlock_bits=0x3F +proMYSBL.bootloader.lock_bits=0x0F +proMYSBL.bootloader.file=MySensors/MYSBootloader.hex + +proMYSBL.build.mcu=atmega328p +proMYSBL.build.f_cpu=16000000L +proMYSBL.build.board=AVR_UNO +proMYSBL.build.core=arduino +proMYSBL.build.variant=standard + + + + + From ff1ddc38079d1b619fa0795fe70b425790298a91 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 6 Feb 2015 19:09:51 -0200 Subject: [PATCH 4/9] Relay messages only after receiving an id --- libraries/MySensors/MySensor.cpp | 108 ++++++++++++++----------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index dfaa01c..d0558c5 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -27,11 +27,9 @@ inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, ui return msg; } - MySensor::MySensor(uint8_t _cepin, uint8_t _cspin) : RF24(_cepin, _cspin) { } - void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, uint8_t _parentNodeId, rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { Serial.begin(BAUD_RATE); isGateway = false; @@ -125,7 +123,6 @@ void MySensor::setupRepeaterMode(){ eeprom_read_block((void*)childNodeTable, (void*)EEPROM_ROUTES_ADDRESS, 256); } - uint8_t MySensor::getNodeId() { return nc.nodeId; } @@ -141,7 +138,6 @@ void MySensor::requestNodeId() { waitForReply(); } - void MySensor::findParentNode() { failedTransmissions = 0; @@ -228,7 +224,6 @@ boolean MySensor::sendWrite(uint8_t next, MyMessage &message, bool broadcast) { return ok; } - bool MySensor::send(MyMessage &message, bool enableAck) { message.sender = nc.nodeId; mSetCommand(message,C_SET); @@ -262,7 +257,6 @@ void MySensor::requestTime(void (* _timeCallback)(unsigned long)) { sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set("")); } - boolean MySensor::process() { uint8_t pipe; boolean available = RF24::available(&pipe); @@ -289,18 +283,7 @@ boolean MySensor::process() { uint8_t last = msg.last; uint8_t destination = msg.destination; - if (repeaterMode && command == C_INTERNAL && type == I_FIND_PARENT) { - if (nc.distance == 255) { - findParentNode(); - } else if (sender != nc.parentNodeId) { - // Relaying nodes should always answer ping messages - // Wait a random delay of 0-2 seconds to minimize collision - // between ping ack messages from other relaying nodes - delay(millis() & 0x3ff); - sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true); - } - return false; - } else if (destination == nc.nodeId) { + if (destination == nc.nodeId) { // Check if sender requests an ack back. if (mGetRequestAck(msg)) { // Copy message @@ -390,54 +373,66 @@ boolean MySensor::process() { } // Return true if message was addressed for this node... return true; - } else if (repeaterMode && pipe == CURRENT_NODE_PIPE) { - // We should try to relay this message to another node + } else if (repeaterMode && nc.nodeId != AUTO) { + // Relaying nodes should answer only after set an id - uint8_t route = getChildRoute(msg.destination); - if (route>0 && route<255) { - // This message should be forwarded to a child node. If we send message - // to this nodes pipe then all children will receive it because the are - // all listening to this nodes pipe. - // - // +----B - // -A - // +----C------D - // - // We're node C, Message comes from A and has destination D - // - // lookup route in table and send message there - sendWrite(route, msg); - } else if (sender == GATEWAY_ADDRESS && destination == BROADCAST_ADDRESS) { - // A net gateway reply to a message previously sent by us from a 255 node - // We should broadcast this back to the node - sendWrite(destination, msg, true); - } else { - // A message comes from a child node and we have no - // route for it. - // - // +----B - // -A - // +----C------D <-- Message comes from D - // - // We're node C - // - // Message should be passed to node A (this nodes relay) + if (command == C_INTERNAL && type == I_FIND_PARENT) { + if (nc.distance == 255) { + findParentNode(); + } else if (sender != nc.parentNodeId) { + // Relaying nodes should always answer ping messages + // Wait a random delay of 0-2 seconds to minimize collision + // between ping ack messages from other relaying nodes + delay(millis() & 0x3ff); + sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true); + } + } else if (pipe == CURRENT_NODE_PIPE) { + // We should try to relay this message to another node - // This message should be routed back towards sensor net gateway - sendWrite(nc.parentNodeId, msg); - // Add this child to our "routing table" if it not already exist - addChildRoute(sender, last); + uint8_t route = getChildRoute(msg.destination); + if (route>0 && route<255) { + // This message should be forwarded to a child node. If we send message + // to this nodes pipe then all children will receive it because the are + // all listening to this nodes pipe. + // + // +----B + // -A + // +----C------D + // + // We're node C, Message comes from A and has destination D + // + // lookup route in table and send message there + sendWrite(route, msg); + } else if (sender == GATEWAY_ADDRESS && destination == BROADCAST_ADDRESS) { + // A net gateway reply to a message previously sent by us from a 255 node + // We should broadcast this back to the node + sendWrite(destination, msg, true); + } else { + // A message comes from a child node and we have no + // route for it. + // + // +----B + // -A + // +----C------D <-- Message comes from D + // + // We're node C + // + // Message should be passed to node A (this nodes relay) + + // This message should be routed back towards sensor net gateway + sendWrite(nc.parentNodeId, msg); + // Add this child to our "routing table" if it not already exist + addChildRoute(sender, last); + } } } return false; } - MyMessage& MySensor::getLastMessage() { return msg; } - void MySensor::saveState(uint8_t pos, uint8_t value) { if (loadState(pos) != value) { eeprom_write_byte((uint8_t*)(EEPROM_LOCAL_CONFIG_ADDRESS+pos), value); @@ -447,7 +442,6 @@ uint8_t MySensor::loadState(uint8_t pos) { return eeprom_read_byte((uint8_t*)(EEPROM_LOCAL_CONFIG_ADDRESS+pos)); } - void MySensor::addChildRoute(uint8_t childId, uint8_t route) { if (childNodeTable[childId] != route) { childNodeTable[childId] = route; @@ -466,7 +460,6 @@ uint8_t MySensor::getChildRoute(uint8_t childId) { return childNodeTable[childId]; } - int8_t pinIntTrigger = 0; void wakeUp() //place to send the interrupts { @@ -589,7 +582,6 @@ void MySensor::debugPrint(const char *fmt, ... ) { } #endif - #ifdef DEBUG int MySensor::freeRam (void) { extern int __heap_start, *__brkval; From 381d98648f7021bfc19cd033a27928118bc58cc9 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 6 Feb 2015 20:59:57 -0200 Subject: [PATCH 5/9] fix clear routing table data childNodeTable is a pointer, will always return 2 --- libraries/MySensors/MySensor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index dfaa01c..e8c9ea6 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -365,7 +365,7 @@ boolean MySensor::process() { if (repeaterMode && msg.getString()[0] == 'C') { // Clears child relay data for this node debug(PSTR("rd=clear\n")); - for (uint8_t i=0;i< sizeof(childNodeTable); i++) { + for (uint8_t i=0;i< 256; i++) { removeChildRoute(i); } // Clear parent node id & distance to gw From ace0a38c93be4bc622a998b6247e0499b8f29575 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Sat, 7 Feb 2015 10:59:25 -0200 Subject: [PATCH 6/9] Node set up only after having an id --- libraries/MySensors/MySensor.cpp | 41 ++++++++++++++++++-------------- libraries/MySensors/MySensor.h | 1 + 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index f61a12d..d3d28a3 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -78,20 +78,12 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b debug(PSTR("%s started, id %d\n"), repeaterMode?"repeater":"sensor", nc.nodeId); - // Open reading pipe for messages directed to this node (set write pipe to same) - RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId)); - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); - - // Send presentation for this radio node (attach - present(NODE_SENSOR_ID, repeaterMode? S_ARDUINO_REPEATER_NODE : S_ARDUINO_NODE); - - // Send a configuration exchange request to controller - // Node sends parent node. Controller answers with latest node configuration - // which is picked up in process() - sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(nc.parentNodeId)); - - // Wait configuration reply. - waitForReply(); + // If we got an id, set this node to use it + if (nc.nodeId != AUTO) { + setupNode(); + // Wait configuration reply. + waitForReply(); + } } void MySensor::setupRadio(rf24_pa_dbm_e paLevel, uint8_t channel, rf24_datarate_e dataRate) { @@ -138,6 +130,20 @@ void MySensor::requestNodeId() { waitForReply(); } +void MySensor::setupNode() { + // Open reading pipe for messages directed to this node (set write pipe to same) + RF24::openReadingPipe(WRITE_PIPE, TO_ADDR(nc.nodeId)); + RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); + + // Send presentation for this radio node (attach + present(NODE_SENSOR_ID, repeaterMode? S_ARDUINO_REPEATER_NODE : S_ARDUINO_NODE); + + // Send a configuration exchange request to controller + // Node sends parent node. Controller answers with latest node configuration + // which is picked up in process() + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(nc.parentNodeId)); +} + void MySensor::findParentNode() { failedTransmissions = 0; @@ -325,15 +331,14 @@ boolean MySensor::process() { } else if (type == I_ID_RESPONSE) { if (nc.nodeId == AUTO) { nc.nodeId = msg.getByte(); - // Write id to EEPROM if (nc.nodeId == AUTO) { // sensor net gateway will return max id if all sensor id are taken debug(PSTR("full\n")); while (1); // Wait here. Nothing else we can do... - } else { - RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); - eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId); } + setupNode(); + // Write id to EEPROM + eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId); debug(PSTR("id=%d\n"), nc.nodeId); } } else if (type == I_CONFIG) { diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index d96ec13..9d50b4b 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -276,6 +276,7 @@ class MySensor : public RF24 void waitForReply(); void requestNodeId(); + void setupNode(); void findParentNode(); uint8_t crc8Message(MyMessage &message); uint8_t getChildRoute(uint8_t childId); From a6301f85218dc43c277a60432a9d204322186154 Mon Sep 17 00:00:00 2001 From: tekka007 Date: Sat, 7 Feb 2015 21:42:49 +0100 Subject: [PATCH 7/9] Fixing endless loop --- libraries/MySensors/MySensor.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index d3d28a3..b642313 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -353,9 +353,10 @@ boolean MySensor::process() { if (repeaterMode && msg.getString()[0] == 'C') { // Clears child relay data for this node debug(PSTR("rd=clear\n")); - for (uint8_t i=0;i< 256; i++) { + uint8_t i = 255; + do { removeChildRoute(i); - } + } while (i--); // Clear parent node id & distance to gw eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, 0xFF); eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, 0xFF); From 08a0bc25ff375d38bb4cf9534819b42fb86069ea Mon Sep 17 00:00:00 2001 From: tekka007 Date: Sun, 8 Feb 2015 01:00:39 +0100 Subject: [PATCH 8/9] simply wait() & adding watchdog resets Simplify wait(), added watchdog reset to wait() and waitForReply(). Substituted all waitForReply() for wait(2000) to reduce code size/complexity --- libraries/MySensors/MySensor.cpp | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index b642313..a330fb6 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -82,7 +82,7 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b if (nc.nodeId != AUTO) { setupNode(); // Wait configuration reply. - waitForReply(); + wait(2000); } } @@ -127,7 +127,7 @@ void MySensor::requestNodeId() { debug(PSTR("req node id\n")); RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("")); - waitForReply(); + wait(2000); } void MySensor::setupNode() { @@ -155,13 +155,15 @@ void MySensor::findParentNode() { sendWrite(BROADCAST_ADDRESS, msg, true); // Wait for ping response. - waitForReply(); + wait(2000); } void MySensor::waitForReply() { unsigned long enter = millis(); // Wait a couple of seconds for response while (millis() - enter < 2000) { + // reset watchdog + wdt_reset(); process(); } } @@ -498,19 +500,13 @@ void MySensor::sleep(unsigned long ms) { } void MySensor::wait(unsigned long ms) { - bool slept_enough = false; - unsigned long start = millis(); - unsigned long now; - // Let serial prints finish (debug, log etc) Serial.flush(); - - while (!slept_enough) { - MySensor::process(); - now = millis(); - if (now - start > ms) { - slept_enough = true; - } + unsigned long enter = millis(); + while (millis() - enter < ms) { + // reset watchdog + wdt_reset(); + process(); } } From 7b99a2ef94e7cfc77d8105f2b6e706eb210b4350 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Tue, 10 Feb 2015 22:25:13 +0100 Subject: [PATCH 9/9] Removed waitForReply(). Not needed anymore. --- libraries/MySensors/MySensor.cpp | 10 ---------- libraries/MySensors/MySensor.h | 1 - 2 files changed, 11 deletions(-) diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index a330fb6..a2a63b5 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -158,16 +158,6 @@ void MySensor::findParentNode() { wait(2000); } -void MySensor::waitForReply() { - unsigned long enter = millis(); - // Wait a couple of seconds for response - while (millis() - enter < 2000) { - // reset watchdog - wdt_reset(); - process(); - } -} - boolean MySensor::sendRoute(MyMessage &message) { // Make sure to process any incoming messages before sending (could this end up in recursive loop?) // process(); diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 9d50b4b..27d4777 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -274,7 +274,6 @@ class MySensor : public RF24 void (*timeCallback)(unsigned long); // Callback for requested time messages void (*msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway. - void waitForReply(); void requestNodeId(); void setupNode(); void findParentNode();