Arduino ProMini 3.3V on 1MHz + RFM69W missing ACKs
-
Hi everyone,
This is my first post here. First of all, I should say congratulation for the formidable work you're putting in there. I've discovered MySensors about 2 months ago and I'm loving it.
I've recently built:
- A MQTT gateway with a NodeMCU + RFM69W
- A temperature node (Arduino ProMini 3.3v) + SI7021 + RFM69W. Running on a CR2032 cell.
Initially tested the whole thing for a few days and it works like a charm. But I noticed battery level dropping slowly (well expected I suppose). I thought, maybe I can actually do something to minimize energy used. I read a bunch online and implemented the following:
- Remove the led (well that one everyone seems to remove)
- Remove the regulator
- Add 100uF capacitor between GND and 3.3V on RFM69W (helps battery I've heard)
- Add sleep instructions here and there in my code to allow the battery to rest. The code is mostly the SI7021 example found in MySensors plus a few sleeps...
- Disable BOD (otherwise actually noticed that since adding my capacitor, my sensor would struggle to start, because of initial drain taking the battery to ~2.7V I suppose)
- Lower CPU clock to 1MHz to allow to run when battery gets lower voltage (I read that 8MHz wouldn't be stable around 1.8V but the radio, sensor and arduino could still work at that voltage).
Now, it still works. And if anyone has any feedback on what I just explained, they would be most welcome as I'm sure I've got more to learn.
One thing doesn't work as expected: when running on 1MHz, most of the time, my Gateway receives duplicate transmissions of my node. When running on 8MHz (everything else the same), no duplicates. Sensor is close to GW (3m). I suspected maybe on 1MHz I need to increase the ACK timeout of sensor, so I increased it to 2seconds. I can now see duplicate messages being 2s apart on the gateway, so that didn't do the trick.
My current solution is to not allow retries, but that is really a bad workaround... Anyone has an idea of what I could try to see what is the problem here?
Thanks everyone.
-
Could you post your code? You say you have added sleep statements, I'm wondering where you placed them.
If you see duplicate messages, the hardware ack as sent by the gateway is not received by the module. So you should check the gateway also. Power supply, antenna, these kind of things
-
There it is:
/** * The MySensors Arduino library handles the wireless radio link and protocol * between your home built sensors/actuators and HA controller of choice. * The sensors forms a self healing radio network with optional repeaters. Each * repeater and gateway builds a routing tables in EEPROM which keeps track of the * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad <henrik.ekblad@mysensors.org> * Copyright (C) 2013-2015 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org * Support Forum: http://forum.mysensors.org * * 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. * ******************************* * * REVISION HISTORY * Version 1.0: Yveaux * * DESCRIPTION * This sketch provides an example of how to implement a humidity/temperature * sensor using a Si7021 sensor. * * For more information, please visit: * http://www.mysensors.org/build/humiditySi7021 * */ // Enable debug prints //#define MY_DEBUG // Enable REPORT_BATTERY_LEVEL to measure battery level and send changes to gateway #define REPORT_BATTERY_LEVEL // Enable and select radio type attached //#define MY_RADIO_RF24 #define MY_RADIO_RFM69 //#define MY_RS485 #define MY_RFM69_FREQUENCY (RFM69_433MHZ) #define MY_NODE_ID 1 #define MY_PARENT_NODE_ID 0 #define MY_PARENT_NODE_IS_STATIC #include <MySensors.h> #define CHILD_ID_HUM 0 #define CHILD_ID_TEMP 1 static bool metric = true; // Sleep time between sensor updates (in milliseconds) static const uint64_t UPDATE_INTERVAL = 180000; static const uint64_t COMM_INTERVAL = 60000; // Sleep between communications to rest battery and charge capacitor static const uint16_t UPDATE_COUNT = 100; // Will force update every UPDATE_COUNT cycles #include <SI7021.h> static SI7021 sensor; #ifdef REPORT_BATTERY_LEVEL #include <Vcc.h> static uint16_t currentCount = 0; // Initialize at 0 static uint8_t oldBatteryPcnt = 200; // Initialize to 200 to assure first time value will be sent. static float oldTemp = 100; // Initialize to 100 to ensure first time value will be reported static float oldHum = 200; // Initialize to 200 to ensure first time value will be reported const float VccMin = 1.8; // Minimum expected Vcc level, in Volts: Brownout at 1.8V -> 0% const float VccMax = 3; //2.0*1.6; // Maximum expected Vcc level, in Volts: 2xAA fresh Alkaline -> 100% const float VccCorrection = 1.0; // Measured Vcc by multimeter divided by reported Vcc static Vcc vcc(VccCorrection); #endif void presentation() { // Send the sketch info to the gateway //sleep(COMM_INTERVAL); //sendSketchInfo("TemperatureAndHumidity", "1.0"); // Present sensors as children to gateway sleep(2*COMM_INTERVAL); present(CHILD_ID_HUM, S_HUM, "Humidity"); sleep(COMM_INTERVAL); present(CHILD_ID_TEMP, S_TEMP, "Temperature"); sleep(COMM_INTERVAL); metric = getControllerConfig().isMetric; } void setup() { while (not sensor.begin()) { Serial.println(F("Sensor not detected!")); delay(5000); } sleep(COMM_INTERVAL); } void loop() { // Read temperature & humidity from sensor. const float temperature = float( metric ? sensor.getCelsiusHundredths() : sensor.getFahrenheitHundredths() ) / 100.0; const float humidity = float( sensor.getHumidityBasisPoints() ) / 100.0; #ifdef MY_DEBUG Serial.print(F("Temp ")); Serial.print(temperature); Serial.print(metric ? 'C' : 'F'); Serial.print(F("\tHum ")); Serial.println(humidity); #endif static MyMessage msgHum( CHILD_ID_HUM, V_HUM ); static MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP); // Temperature granularity will depend on temperature range if ((abs(temperature - oldTemp) > 0.5) || (currentCount > UPDATE_COUNT)) { send(msgTemp.set(temperature, 2)); oldTemp = temperature; sleep(COMM_INTERVAL); } else if ((abs(temperature - 22.25) <= 0.75) && (abs(temperature - oldTemp) > 0.15)) { send(msgTemp.set(temperature, 2)); oldTemp = temperature; sleep(COMM_INTERVAL); } if ((abs(humidity - oldHum) > 5.0) || (currentCount > UPDATE_COUNT)) { send(msgHum.set(humidity, 2)); oldHum = humidity; sleep(COMM_INTERVAL); } if (currentCount > UPDATE_COUNT) { currentCount = 0; } currentCount++; #ifdef REPORT_BATTERY_LEVEL sleep(COMM_INTERVAL); const uint8_t batteryPcnt = static_cast<uint8_t>(0.5 + vcc.Read_Perc(VccMin, VccMax)); #ifdef MY_DEBUG Serial.print(F("Vbat ")); Serial.print(vcc.Read_Volts()); Serial.print(F("\tPerc ")); Serial.println(batteryPcnt); #endif if ( abs(batteryPcnt - oldBatteryPcnt) >= 0.035) { sendBatteryLevel(batteryPcnt); oldBatteryPcnt = batteryPcnt; } #endif // Sleep until next update to save energy sleep(UPDATE_INTERVAL); }
When I have time I'll look at the gateway. However, I'm dubious this is it because when I keep everything else the same, but change frequency back to 8MHz (by changing node bootloader), I don't get duplicates anymore.
-
The long sleeps in between your send functions are not really needed, if you would like some time to charge the capacitor (this is done in milliseconds), a delay will also do.
But that doesn't explain the duplicate messages. Can you post debug log for both situations? If you suspect the drivers, it could be better to open an issue on GitHub
-
I'll post logs this weekend hopefully.
I take your point on charging the capacitor. It looks like 1ms would be more than enough to charge it completely. I suppose the question is also, how much time does it take for the battery to recover from a send operation? I don't have the right tooling to do a nice numerical analysis on that, but maybe can do something cheeky.
-
@thenounoursk I really advise You to get a good multimeter with current measurement down to 1uA. Without it You will have hard time to debug problems with high currrent consumption. And I mean high current is about 100uA for a battery powered node. Waiting couple of days to check if current cosumption problem is fixed is not a good idea There can be a lot of issues that makes your node consume to much current, both hardware and software. For example, I have a node that had good current consumption, but after some time it started to cosume much more current. It turned out to be a failed power decoupling capacitor. Without multimeter I would never find out what was the cause.
-
@rozpruwacz I should have been more precise, I have a good multimeter already But when I talked about how measuring how much time it takes the battery to recover after a send operation, I thought about doing it on an oscilloscope... Poor phrasing on my part, my multimeter will be enough to get an idea of how much time it takes, I will just not get the nice chart to back up my experience.
-
Hi, as a side note - check the datasheet of the 100uf capacitor you have used for the leakage current as it could be as high a few uA which is comparable with the current of a sleeping ATMega328 / Arduino board. Make sure you use a low leakage capacitor for a longer battery life.
For a low current temperature sensor you can use a thermistor (like 4k7 @ 25 Celsius) in series with a resistor (4k7 1%). Connect one end of the th to the GND, one end of the resistor to a digital output of the Arduino and the common connection of the th and resistor to an analog input of Arduino. You will power on (make the output HIGH) the th+resistor string from the digital out shortly before making the measurement then make the output LOW after the measurement.
Means you will only use power to measure the temperature for a very short time.
Here is my code (I am not a professional firmware guy ) for such a temperature sensor:// ver. v1.2 /* Mini v5 TH: Th=GND, Series Resistor=3, Middle=A0 check Radio connections/wires check power supply +5V */ //Enable debug prints to serial monitor //#define MY_DEBUG #define MY_RADIO_NRF24 #define MY_NODE_ID 9 #define DEBUG 0 #define BATTERY_SENSOR 1 #include <MySensors.h> #include <Vcc.h> #define TH_LIBRARY_CHILD_ID 1 #define VOLTAGE_CHILD_ID 2 #define TH_PIN 3 int _nominal_resistor = 4700; int _nominal_temperature = 25; int _b_coefficient = 3950; int _series_resistor = 4877; int _pin = 0; int batt_report_count = 120; const float VccMin = 1.8; const float VccMax = 3.0; const float VccCorrection = 1.0 / 1.0; // Measured Vcc by multimeter divided by reported Vcc Vcc vcc(VccCorrection); MyMessage msgTemp(TH_LIBRARY_CHILD_ID, V_TEMP); MyMessage msgVoltage(VOLTAGE_CHILD_ID, V_VOLTAGE); void setup() { //Serial.begin(115200); } void presentation() { // Send the sketch version information to the gateway and Controller sendSketchInfo("TH_LIBRARY@Mini_2xR6", "1.2_FOTA"); wait(1); present(TH_LIBRARY_CHILD_ID, S_TEMP, "TH_LIBRARY"); wait(1); present(VOLTAGE_CHILD_ID, S_MULTIMETER, "TH_LIBRARY_BATT"); wait(1); } void loop() { if (batt_report_count == 120) { batteryReport(); } batt_report_count--; if (batt_report_count == 0) { batt_report_count = 120; } tempReport(); smartSleep(180000); } //############################################################### // Send a battery level report to the controller void batteryReport() { // measure the board vcc float v = vcc.Read_Volts(); float p = vcc.Read_Perc(VccMin, VccMax); #if DEBUG Serial.print("VCC = "); Serial.print(v); Serial.println(" Volts"); Serial.print("VCC = "); Serial.print(p); Serial.println(" %"); #endif #if BATTERY_SENSOR // report battery voltage send(msgVoltage.set(v, 3)); wait(1); #endif // report battery level percentage sendBatteryLevel(p); wait(1); } //################################################################## void tempReport() { // set TH pin in output mode pinMode(TH_PIN, OUTPUT); // set TH_PIN HIGH digitalWrite(TH_PIN, HIGH); wait(1); // read the voltage across the thermistor float adc = analogRead(_pin); // set TH_PIN LOW digitalWrite(TH_PIN, LOW); // calculate the temperature float reading = (1023 / adc) - 1; reading = _series_resistor / reading; float temperature; temperature = reading / _nominal_resistor; // (R/Ro) temperature = log(temperature); // ln(R/Ro) temperature /= _b_coefficient; // 1/B * ln(R/Ro) temperature += 1.0 / (_nominal_temperature + 273.15); // + (1/To) temperature = 1.0 / temperature; // Invert temperature -= 273.15; // convert to C #if DEBUG == 1 Serial.print(F("THER I=")); Serial.print(TH1_CHILD_ID); Serial.print(F(" V=")); Serial.print(adc); Serial.print(F(" T=")); Serial.println(temperature); #endif send(msgTemp.set(temperature, 1)); wait(1); }
-
@thenounoursk did you ever solve the issue of the duplicate messages on 1mhz?
-
@electrik no not yet. It's still on my list of things to do though.
-
In the mean time I got it working. I thought I was using the HW model, but it turned out that it was a regular W model. Also I'm using the new driver, and this works perfectly.
Sometimes I do receive duplicate messages but I believe that is caused by the connection itself i.e. no acknowledge is received by the transmitter, and that resends the message.
-
Thanks for the tip, I should give a try with the new driver then.
My node actually died a week ago, battery dead. Thing is, a week before that, I checked the battery with a multimeter and it looked in real good shape. So I'm not sure what happened but I'm leaning towards something unexpected rather normal life cycle of a battery. Out of interest, what's your typical lifetime (assuming your setup is similar)?
-
I can't tell yet. The node I'm now using is a wall switch, operating on a CR2032 battery. No idea yet how long it lasts.