I have a moteino with flash chip and RFM69HCW transciever and an AM2315 humidity and temperature sensor and I'm also going to connect it to a rain bucket and wind vane and anemometer. The AM2315 is I2C and the rain and anemometer are interrupt driven devices. I have the rain bucket on pin 3 (int 1) and the anemometer on pin 4 with pinchangeint library running the show there. What I have noticed is that my code keeps cycling through setup even though I have tried disabling all the sleep functions. I've tried disabling the watchdog but I don't think it's working. Can someone look at my code and tell me where I'm going wrong here? I need to keep the moteino awake because wind and rain can happen at any time so I can't miss anything by waking it up on an interrupt only to miss the event.
#include <Adafruit_AM2315.h>
#include <SPIFlash.h>
#include <RFM69.h> //get it here: https://www.github.com/lowpowerlab/rfm69
#include <RFM69_ATC.h>
#include <SPI.h>
#include <Arduino.h> // assumes Arduino IDE v1.0 or greater
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <Wire.h>
#include <stdlib.h>
#include <PinChangeInt.h>
/* RFM69 library and code by Felix Rusu - [email protected]
// Get libraries at: https://github.com/LowPowerLab/
// Make sure you adjust the settings in the configuration section below !!!
// **********************************************************************************
// Copyright Felix Rusu, LowPowerLab.com
// Library and code by Felix Rusu - [email protected]
// **********************************************************************************
// 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.
*/
//watchdog interrupt
/*uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) \
__attribute__((naked)) \
__attribute__((section(".init3")));
void get_mcusr(void)
{
mcusr_mirror = MCUSR;
MCUSR = 0;
wdt_disable();
}*/
ISR (WDT_vect) {
wdt_disable();
}
//#include <LowPower.h> //get library from: https://github.com/lowpowerlab/lowpower
//writeup here: http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/
//*********************************************************************************************
// *********** IMPORTANT SETTINGS - YOU MUST CHANGE/ONFIGURE TO FIT YOUR HARDWARE *************
//*********************************************************************************************
#define NETWORKID 100 // The same on all nodes that talk to each other
#define NODEID 2 // The unique identifier of this node
#define RECEIVER 1 // The recipient of packets
//Match frequency to the hardware version of the radio on your Feather
//#define FREQUENCY RF69_433MHZ
//#define FREQUENCY RF69_868MHZ
#define FREQUENCY RF69_915MHZ
#define ENCRYPTKEY "1ClouDRiveRfaRms" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HCW true // set to 'true' if you are using an RFM69HCW module
//*********************************************************************************************
#define SERIAL_BAUD 115200
#define RFM69_CS 10
#define RFM69_IRQ 2
#define RFM69_IRQN 0 // Pin 2 is IRQ 0!
#define RFM69_RST 9
#define LED 13 // onboard blinky
#define now() millis()
#define RAIN_PIN 3
#define RAIN_IRQ 1
#define WSPEED_PIN 4
#define WDIR A0
int16_t packetnum = 0; // packet counter, we increment per xmission
char * outTemp = "";
char * outHumidity = "";
char * tempmqtt = "";
char * humidmqtt = "";
char * drainmqtt = "";
long lastSecond; //The millis counter to see when a second rolls by
byte seconds; //When it hits 60, increase the current minute
byte seconds_2m; //Keeps track of the "wind speed/dir avg" over last 2 minutes array of data
byte minutes; //Keeps track of where we are in various arrays of data
byte minutes_10m; //Keeps track of where we are in wind gust/dir over last 10 minutes array of data
byte windspdavg[120]; //120 bytes to keep track of 2 minute average
long lastWindCheck = 0;
volatile long lastWindIRQ = 0;
volatile byte windClicks = 0;
volatile unsigned long raintime, rainlast, raininterval, rain;
volatile float dailyrainin = 0; // [rain inches so far today in local time]
volatile float rainHour[60]; //60 floating numbers to keep track of 60 minutes of rain
float windSpeed, tempF, humidity, windgustmph, windspdmph_avg2m, windspeedmph;
int winddir, windgustdir;
int winddiravg[120]; //120 ints to keep track of 2 minute average
int winddir_avg2m; // [0-360 2 minute average wind direction]
float windgustmph_10m; // [mph past 10 minutes wind gust mph ]
int windgustdir_10m; // [0-360 past 10 minutes wind gust direction]
float windgust_10m[10]; //10 floats to keep track of 10 minute max
int windgustdirection_10m[10]; //10 ints to keep track of 10 minute max
void rainIRQ(void);
RFM69_ATC radio = RFM69_ATC(RFM69_CS, RFM69_IRQ, IS_RFM69HCW, RFM69_IRQN);
Adafruit_AM2315 am2315;
void setup() {
wdt_disable();
pinMode(RFM69_RST, INPUT);
pinMode(RFM69_CS, OUTPUT);
digitalWrite(RFM69_CS, LOW);
pinMode(RAIN_PIN, INPUT_PULLUP);
attachInterrupt(RAIN_IRQ, rainIRQ, FALLING);
pinMode(WSPEED_PIN, INPUT_PULLUP);
attachPinChangeInterrupt(WSPEED_PIN, wspeedIRQ, FALLING);
// while (!Serial); // wait until serial console is open, remove if not tethered to computer
Serial.begin(SERIAL_BAUD);
//AM2315 Stuff
// Give the user a bit of time to bring up the serial window.
delay( 5000 );
// Indicate whether the AM2315 sensor is present or not.
if (! am2315.begin()) {
Serial.println( "Sensor not found, check wiring & pullups!" );
while (1);
}
Serial.println("Arduino RFM69 Transmitter");
/*// Hard Reset the RFM module
pinMode(RFM69_RST, OUTPUT);
digitalWrite(RFM69_RST, HIGH);
delay(100);
digitalWrite(RFM69_RST, LOW);
delay(100);
*/
// Initialize radio
radio.initialize(FREQUENCY,NODEID,NETWORKID);
if (IS_RFM69HCW) {
radio.setHighPower(); // Only for RFM69HCW & HW!
}
// https://lowpowerlab.com/forum/moteino/rfm69hw-bit-rate-settings/msg1979/#msg1979
radio.writeReg(0x03,0x0D); //set bit rade to 9k6
radio.writeReg(0x04,0x05);
//radio.setPowerLevel(10); // power output ranges from 0 (5dBm) to 31 (20dBm)
radio.enableAutoPower(-80);
radio.encrypt(ENCRYPTKEY);
//radio.sleep();
pinMode(LED, OUTPUT);
Serial.print("\nTransmitting at ");
Serial.print(FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
Serial.println(" MHz");
power_timer1_disable();
power_timer2_disable();
//power_twi_disable();
// Indicate whether the AM2315 sensor is present or not.
if (! am2315.begin()) {
Serial.println( "Sensor not found, check wiring & pullups!" );
while (1);
}
//Serial.print( "Exit Setup" );
}
void loop() {
//Serial.println("Start loop");
//Serial.print("Hum: "); Serial.println(am2315.readHumidity());
//Serial.print("Temp: "); Serial.println(am2315.readTemperature());
delay(2000); // Wait 1 second between transmits, could also 'sleep' here!
//---------------------------------------------------------
// Get the sensor's humidity value.
float humidity = am2315.readHumidity();
// Get the sensor's temperature value in Celsius and Fahrenheit.
float tempC = am2315.readTemperature();
float tempF = ((tempC * 9)/5)+32;
if(millis() - lastSecond >= 1000)
{
//getWeather();
lastSecond += 1000;
//Take a speed and direction reading every second for 2 minute average
if(++seconds_2m > 119) seconds_2m = 0;
//Calc the wind speed and direction every second for 120 second to get 2 minute average
float currentSpeed = get_wind_speed();
windspeedmph = currentSpeed;
int currentDirection = get_wind_direction();
windspdavg[seconds_2m] = (int)currentSpeed;
winddiravg[seconds_2m] = currentDirection;
//Check to see if this is a gust for the minute
if(currentSpeed > windgust_10m[minutes_10m])
{
windgust_10m[minutes_10m] = currentSpeed;
windgustdirection_10m[minutes_10m] = currentDirection;
}
//Check to see if this is a gust for the day
if(currentSpeed > windgustmph)
{
windgustmph = currentSpeed;
windgustdir = currentDirection;
}
if(++seconds > 59)
{
seconds = 0;
if(++minutes > 59) minutes = 0;
if(++minutes_10m > 9) minutes_10m = 0;
rainHour[minutes] = 0; //Zero out this minute's rainfall amount
windgust_10m[minutes_10m] = 0; //Zero out this minute's gust
}
//Get readings from all sensors
getWeather();
char tempmqtt[30] = "weather/outTemp:";
char hummqtt[30] = ",outHumidity:";
char drainmqtt[30] = ",rain:";
char wspdmqtt[30] = ",windSpeed:";
dtostrf(tempF, 5, 1, &tempmqtt[16]);
dtostrf(humidity, 5, 1, &hummqtt[13]);
dtostrf(dailyrainin, 5, 3, &drainmqtt[6]);
dtostrf(windSpeed, 4, 1, &wspdmqtt[11]);
Serial.println(windClicks);
Serial.println(dailyrainin);
Serial.println(drainmqtt);
//Construct MQTT message
Serial.println(tempmqtt);
strcat(tempmqtt, hummqtt);
Serial.println(tempmqtt);
strcat(tempmqtt,drainmqtt);
//strcat(tempmqtt,drainmqtt);
Serial.println(tempmqtt);
strcat(tempmqtt,wspdmqtt);
Serial.println(tempmqtt);
strcat(tempmqtt,"!");
Serial.println(tempmqtt);
Serial.print( "Transmit Level: " );
Serial.println(radio._transmitLevel);
//char radiopacket[50] = "weather/outTemp:56,outHumidity:56!";
//radiopacket = tempmqtt;
int radpk_len = strlen(tempmqtt) + 1;
itoa(packetnum++, tempmqtt+radpk_len, 10);
//Serial.print("Sending "); Serial.println(radiopacket);
Serial.print("Sending "); Serial.println(tempmqtt);
/*if (radio.sendWithRetry(RECEIVER, radiopacket, strlen(radiopacket))) { //target node Id, message as string or byte array, message length
Serial.println("OK");
}*/
if (radio.sendWithRetry(RECEIVER, tempmqtt, strlen(tempmqtt))) { //target node Id, message as string or byte array, message length
Serial.println("OK");
}
//send the message without retrying
// radio.send(RECEIVER, radiopacket, strlen(radiopacket));//target node Id, message as string or byte array, message length
// Serial.println("OK, sent without retrying");
radio.receiveDone(); //put radio in RX mode
Serial.println("Radio going to sleep");
//radio.sleep();
delay(1000);
//Serial.println("Everything going to sleep");
Serial.flush(); //make sure all serial data is clocked out before sleeping the MCU
//deepsleep();
}
}
You don't say how far through setup() you get or how your mote is powered, both might be important.
The one thing I did notice is that your radio reset code should be treating the pin as a input most of the time (it reflects the radio's reset cycle state) with it being driven to a solid level for only a brief period of time to trigger reset. I don't recall the level at the moment and you should read the RFM69's datasheet on this matter anyway...
Tom
Appreciate the reply Tom, the code functions (I had to omit some of the function calls because it exceeded the length for the forum), but after it finishes the loop it resets again and I can see it going through setup every cycle. I have it powered off the FTDI right now, so maybe that is my issue? I have an enormous 8000 mWh lipo I could power it off of instead, could it simply be that it's resetting after transmitting? It transmits the information to my gateway successfully, but I know it's resetting because my accumulated rain variable resets to 0 after each cycle. Can you comment if what I'm doing with the watchdog is appropriate?
Is this a LowPowerLab FTDI stock adapter?
Then that passes 5V from the USB, the Moteino LDO can supply up to 250mA and that should be plenty.
Otherwise if the VIN rail collapses below 2.7V, it will enter brown out and reset once voltage is back to normal, (stock Moteino fuses).
The new Moteino-8Mhz has 1.8v brown out.
Hey Felix, I got the ftdi from you but I'm also powering a voltage booster to supply the am2315 which it needs. Based on your comments I'll try powering the booster separately to test this out. It kind of makes sense because the last thing that happens in the loop is the transmit which taxes the supply the most. Stay tuned.
Quote from: raenrfm on September 29, 2017, 10:58:33 PM
Hey Felix, I got the ftdi from you but I'm also powering a voltage booster to supply the am2315 which it needs. Based on your comments I'll try powering the booster separately to test this out. It kind of makes sense because the last thing that happens in the loop is the transmit which taxes the supply the most. Stay tuned.
Alternative quick test would be to reduce the transmit power and that will substantially reduce the load enough to keep the voltage from collapsing too far.
Tom
Quote from: raenrfm on September 29, 2017, 10:58:33 PM
Hey Felix, I got the ftdi from you but I'm also powering a voltage booster to supply the am2315 which it needs. Based on your comments I'll try powering the booster separately to test this out. It kind of makes sense because the last thing that happens in the loop is the transmit which taxes the supply the most. Stay tuned.
You need to watch current pulses with boost regulators, they can somtimes take a lot of current during their startup phase. They often take many hundred milliamps by design. Best to use soft start regulators, but even these can be troublesome if the value is fixed in the chip as they're invariably too high. There is a technique using a slew rate controlled high side switch, and feedback via a capacitor from the output voltage of the regulator rather than the output from the high side switch itself. That works quite well, I got mine down from several hundred milliamps to less than 50.
Mark.
Quote from: perky on September 30, 2017, 02:15:27 PM
There is a technique using a slew rate controlled high side switch, and feedback via a capacitor from the output voltage of the regulator rather than the output from the high side switch itself.
Good tip, Mark. And if you use a series resistor into the gate node, your slew rate is very predictable and controllable to virtually any rise time.
Tom
Quote from: TomWS on September 30, 2017, 03:12:54 PM
Good tip, Mark. And if you use a series resistor into the gate node, your slew rate is very predictable and controllable to virtually any rise time.
Yes, this is how these slew rate controlled high side switches work, and they are characterized accordingly. The key with using them with boost regulators is to take the slew feedback from the regulator's output rather than the output of the switch (i.e. the input supply to the regulator). This is because the regulator won't start switching until it reaches a certain level, and when it does it tends to pull the input supply down and that messes with switch which is trying to slew rate control the rise of that supply. You can still get significant current pulses. Taking the feedback from the regulator's output eliminates that effect.
Mark.
Ok so I used an external supply for everything other than the moteino and it's still cycling through setup. Is it my code?
Quote from: raenrfm on October 01, 2017, 11:15:13 AM
Ok so I used an external supply for everything other than the moteino and it's still cycling through setup. Is it my code?
I'll put good money on the series of:
strcat(tempmqtt,drainmqtt);Overflowing the tempmqtt buffer and then the stack, and then the return from loop goes to somewhere where no man has been before...
Tom
I guess my code is too bloated. I'll have to play around with the buffer sizes then to make it work. I'll try a bit of polishing tonight to see if I can get it working. Thanks for the tips.
Quote from: raenrfm on October 02, 2017, 11:17:25 AM
I guess my code is too bloated. I'll have to play around with the buffer sizes then to make it work. I'll try a bit of polishing tonight to see if I can get it working. Thanks for the tips.
The IDE should tell you how much RAM is being used. I doubt that you've run out, but moving your buffers to static space will not only let you know what you're using, it will also reduce the risk of blowing out your stack. Since you can't send more than 62 bytes in a packet, you shouldn't need more than 2X that for transfer buffers.
Tom
When you say "move your buffers to static space" what do you mean? Sorry, I'm still a bit of a newbie.
Reducing the size of my buffers fixed the problem, so your analysis was correct there. I was blowing it up obviously. Now if only I could get my pinchange interrupt on pin 4 working...new thread?
Quote from: raenrfm on October 02, 2017, 10:30:09 PM
When you say "move your buffers to static space" what do you mean?
Rather than declaring your variables inside loop() like:
char tempmqtt[30] = "weather/outTemp:";
char hummqtt[30] = ",outHumidity:";
char drainmqtt[30] = ",rain:";
char wspdmqtt[30] = ",windSpeed:";
which makes the variables 'automatic', placing them in the stack.
Instead, do this:
static char tempmqtt[30] = "weather/outTemp:";
static char hummqtt[30] = ",outHumidity:";
static char drainmqtt[30] = ",rain:";
static char wspdmqtt[30] = ",windSpeed:";
which places them in normal RAM data area and will get measured in the IDE memory report. Another benefit is that they only get initialized once, but that can be a double edged sword since you're appending all the other results into tempmqtt. However, in your case, this problem goes away when you execute:
dtostrf(tempF, 5, 1, &tempmqtt[16]);
since this will add the NULL termination to the end of the tempF value in the string, effectively erasing all the characters that follow it.
Quote
Sorry, I'm still a bit of a newbie.
No problem, we were all newbies at one point!
Quote
Reducing the size of my buffers fixed the problem, so your analysis was correct there. I was blowing it up obviously. Now if only I could get my pinchange interrupt on pin 4 working...new thread?
Yeah, that's probably best as you'll get more focused results.
Tom
Thanks for the assistance Tom, much appreciated! I will try your suggestions.
Tom, now that I sorted out my interrupt issues, I'm still having problems with I think the capabilities of the Moteino. I think what I'm trying to do here with concatenating these converted floats together is just taxing the memory too much. Is there a better way to do what I'm looking to do? Essentially I have the following measurables that I gather from my sensors and some need to be calculated in the mote just because of speed in order to be accurate:
Temperature (will sample probably once a minute no more)
Humidity (same sample rate)
Windspeed and Direction (will be constantly updated, no sleeping here)
Rain (same as wind, needs to be monitored because we can't miss a bucket tip on the sensor)
Then of course I have to bundle all this into a nice tidy radio packet(s) to send to my gateway which is looking for the following format:
topic/label:value,label:value, etc... so in this case: weather/tempF:XX.X, outHumidity:XX.X....etc.
So, is using the dtostrf and strcat functions really the best way to do this? Or is there a more efficient/tidy way to do this so that I'm not pushing up against the memory limits of the poor little Mote?
Forgot to mention that the windgust and windgust directions need to be calculated based on the 10 minute average, and should be done in the Mote before sending it to the gateway. I could try to offload that to my gateway, but then it would really upset the generic mqtt nature of my gateway which I want to preserve.
Quote from: raenrfm on October 09, 2017, 12:15:01 PM
So, is using the dtostrf and strcat functions really the best way to do this? Or is there a more efficient/tidy way to do this so that I'm not pushing up against the memory limits of the poor little Mote?
I rarely use floats in embedded controllers. I've used fixed point values for so long that you probably weren't even born... uh, never mind...
The temp/humidity sensor you're using is a good example of this - the accuracy of the humidity sensor is +/- 2%. Do you REALLY need a float to represent this? The temp sensor, IIRC is 1/16 degree C resolution. Keep it at that value until you're ready to convert to 'float' Fahrenheit. Then convert to 16bit integer and scale up by 10 or 100 so the LSBs aren't lost and do the math to convert to Fahrenheit hundreths. Then the value you send is in two parts: Integer part is temp/100. Fractional part is temp%100 - easy peasy and much faster and MUCH less program/data memory than using floating point values. When you move back to your ARM Cortex M4F, then you can use floats...
Also, I can't recall the last time I used strcat... I think it was shortly after I moved away from PL/I. In your case I'd use sprintf() (which you're using anyway), to move the label and value into a buffer, using sprintf's feature of returning the number of characters moved to update your buffer pointer.
For example:
int len;
uint8_t buffer[64];
len =sprintf(buffer,"weather/outtemp:%d.%02d",intTemp,fracTemp);
len+=sprintf(&buffer[len],",outHumidity:%d",humidity);
len+=sprintf(&buffer[len],",rain:%d.%02d",intRain,fracRain);
....
Note that only ONE buffer is used above. And the label values only take up one location. Uunfortunately sprintf won't use PROGMEM data conveniently, but I think you've saved enough with this approach...
Tom
Quote from: TomWS on October 09, 2017, 07:59:19 PM
unfortunately sprintf won't use PROGMEM data conveniently, but I think you've saved enough with this approach...
Are you sure about that? There's sprintf_P and associated PSTR macro, I think the following will work:
len =sprintf_P(buffer,PSTR("weather/outtemp:%d.%02d"),intTemp,fracTemp);
len+=sprintf_P(&buffer[len],PSTR(",outHumidity:%d"),humidity);
len+=sprintf_P(&buffer[len],PSTR(",rain:%d.%02d"),intRain,fracRain);
Mark.
Quote from: perky on October 10, 2017, 11:45:27 PM
Are you sure about that? There's sprintf_P and associated PSTR macro, I think the following will work:
len =sprintf_P(buffer,PSTR("weather/outtemp:%d.%02d"),intTemp,fracTemp);
len+=sprintf_P(&buffer[len],PSTR(",outHumidity:%d"),humidity);
len+=sprintf_P(&buffer[len],PSTR(",rain:%d.%02d"),intRain,fracRain);
Mark.
Thanks Mark, I hadn't used this version so didn't want to recommend it, especially since the technique in its first form is portable. I found this while following up on your post:
https://forum.arduino.cc/index.php?topic=383898.msg2647013#msg2647013
Useful indeed, if you happen to be using a memory anemic AVR processor... or is that redundant?
Tom
Yeah, it's quite handy. The printf_P variant is also extremely useful if you have lots of degug code or menu options to print. I use that by default for printing any literal strings, it can save a lot of SRAM.
Mark.
Tom, so I tried this:
// Get the sensor's humidity value.
float humidity = am2315.readHumidity();
// Get the sensor's temperature value in Celsius and Fahrenheit.
float tempC = am2315.readTemperature();
float tempF = ((tempC * 9)/5)+32;
//Get rid of float
int16_t tempFscaled = tempF*100;
int16_t intTemp = tempFscaled/100;
int16_t fracTemp = tempFscaled%100;
int16_t rainscaled = dailyrainin*1000;
int16_t intRain = rainscaled/1000;
int16_t fracRain = rainscaled%1000;
Serial.println(tempF);
Serial.println(intTemp);
Serial.println(fracTemp);
Serial.println(dailyrainin);
Serial.println(intRain);
Serial.println(fracRain);
int len;
uint8_t buffer[64];
len =sprintf(buffer,"weather/outTemp:%d.%02d",intTemp,fracTemp);
//len+=sprintf(&buffer[len],",outHumidity:%d",humidity);
//len+=sprintf(&buffer[len],",rain:%d.%02d",intRain,fracRain);
Serial.println(len);
and no matter what I'm getting "invalid conversion from 'uint8_t* {aka unsigned char*}' to 'char*' [-fpermissive]. Not sure what I'm doing wrong here?
I fixed it by declaring char buffer[64] instead. Guess it didn't like the uint8_t.
sprintf requires the buffer to be a char*. You could also fix it by casting buffer to uchar* in the sprintf, but if you have other functions that modify buffer and require it to be a uint8_t* you'll probably might want to do that instead. It's quite annoying that things typedef'd to exactly the same thing are flagged up as type errors.
Mark.
Quote from: perky on October 14, 2017, 03:14:17 PM
It's quite annoying that things typedef'd to exactly the same thing are flagged up as type errors.
It's not the same, which is why it's getting flagged. A 'char' is considered a signed 8 bit value (don't ask me why) whereas uint8_t, AKA unsigned char, is unsigned.
Now we all know that sprintf doesn't care about the signedness of the contents of the buffer, but apparently sprintf not overloaded to take either.
Tom
I see what you're saying but it's not sign per se that's causing the error as whether a char is signed or not signed is compiler specific, it's not specified in C. So char, unsigned char and signed char are all different types. The default for gcc is to treat char as signed, but this can be overridden to unsigned. So even if sprintf were over-loaded to take char, unsigned char or signed char, using a uint8_t, which is typedef'd to unsigned char, will still have caused a type mismatch error.
Mark.