Hi
is it possible to have for example two digital pins used eg. 3 and 4 both doing same reading but transmitting its own ID on one Moteino?
Pin 3 - > id node 23
Pin 4 - > id node 24
P.s. I am still newbe on programming level.
Ziga
The radio ID is not fixed, you can change it any time you want. So you can transmit from the same radio with different sender IDs.. if you can keep track of what you're doing :)
Use this to change the ID at any time:
void RFM69::setAddress(byte addr)
Hi Felix
Tnx for the answer just one more thing to be more clear what I am trying to achieve.
Background:
On my water meter I have impulse module connected from the company that sold me the water meter its self. It gives one interrupt every 10 L
I connected this module on digital pin 3 and GND. The original code was modified with the following
/*
WaterMote_RFM69.cpp - Arduino/Moteino sketch for reading a SY310 based water/pulse meter
Copyright (c) 2013 Felix Rusu ([email protected]). All rights reserved.
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <EEPROM.h>
#include <TimerOne.h>
#include <RFM69.h>
#include <SPI.h>
#define NODEID 24 //network ID used for this unit
#define NETWORKID 100 //the network ID we are on
#define GATEWAYID 1 //the node ID we're sending to
#define FREQUENCY RF69_868MHZ// RF69_433MHZ //others: RF69_868MHZ, RF69_915MHZ
#define ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
#define LED 9
#define INPUTPIN 3 //INT1 = digital pin 3 (must be an interrupt pin!)
#define SERIAL_EN //uncomment this line to enable serial IO (when you debug Moteino and need serial output)
#define SERIAL_BAUD 115200
#ifdef SERIAL_EN
#define DEBUG(input) {Serial.print(input);}
#define DEBUGln(input) {Serial.println(input);}
#else
#define DEBUG(input);
#define DEBUGln(input);
#endif
RFM69 radio;
uint8_t KEY[] = "ADBCADBCADBCADBC";
int ledState = LOW;
volatile unsigned long PulseCounterVolatile = 0; // use volatile for shared variables
unsigned long PulseCounter = 0;
float GAL;
byte PulsesPerGAL = 100;
volatile unsigned long NOW = 0;
volatile unsigned long BEFORE = 0;
unsigned long LASTMINUTEMARK = 0;
unsigned long PULSECOUNTLASTMINUTEMARK = 0; //keeps pulse count at the last minute mark
byte COUNTEREEPROMSLOTS = 10;
unsigned long COUNTERADDRBASE = 8; //address in EEPROM that points to the first possible slot for a counter
unsigned long COUNTERADDR = 0; //address in EEPROM that points to the latest Counter in EEPROM
byte secondCounter = 0;
unsigned long TIMESTAMP_pulse_prev = 0;
unsigned long TIMESTAMP_pulse_curr = 0;
int GPMthreshold = 8000; // GPM will reset after this many MS if no pulses are registered
int XMIT_Interval = 5000; // GPMthreshold should be less than 2*XMIT_Interval
int pulseAVGInterval = 0;
int pulsesPerXMITperiod = 0;
float GPM=0;
char sendBuf[MAX_DATA_LEN];
byte sendLen;
int buttonState = 0;
int previous = 0;
int debounce = 0;
void setup() {
//EEPROM_Write_Counter(4365);
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW
radio.setHighPower(); //uncomment only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
radio.sleep(); //sleep right away to save power
pinMode(LED, OUTPUT);
//initialize counter from EEPROM
unsigned long savedCounter = EEPROM_Read_Counter();
if (savedCounter <=0) savedCounter = 1; //avoid division by 0
PulseCounterVolatile = PulseCounter = PULSECOUNTLASTMINUTEMARK = savedCounter;
//attachInterrupt(INPUTPIN, pulseCounter, RISING);
pinMode(INPUTPIN, INPUT_PULLUP);
Timer1.initialize(XMIT_Interval * 1000L);
Timer1.attachInterrupt(XMIT);
#ifdef SERIAL_EN
Serial.begin(SERIAL_BAUD);
DEBUGln("\nTransmitting...");
#endif
}
void XMIT(void)
{
noInterrupts();
PulseCounter = PulseCounterVolatile;
interrupts();
if (millis() - TIMESTAMP_pulse_curr >= 5000)
{ ledState = !ledState; digitalWrite(LED, ledState); }
//calculate Gallons counter
GAL = ((float)PulseCounter)/PulsesPerGAL;
//DEBUG("PulseCounter:");DEBUG(PulseCounter);DEBUGln("");//DEBUG(", GAL: "); DEBUGln(GAL);
String tempstr = (String)PulseCounter;
//String tempstr = String("VOLUMEN:");
//tempstr += (unsigned long)GAL;
//tempstr += '.';
//tempstr += ((unsigned long)(GAL * 100)) % 100;
//calculate & output GPM
//GPM = pulseAVGInterval > 0 ? 60.0 * 1000 * (1.0/PulsesPerGAL)/(pulseAVGInterval/pulsesPerXMITperiod)
// : 0;
//pulsesPerXMITperiod = 0;
//pulseAVGInterval = 0;
//tempstr += " GPM:";
//tempstr += (int)GPM;
//tempstr += '.';
//tempstr += ((int)(GPM * 100)) % 100;
//secondCounter += XMIT_Interval/1000;
//once per minute, output a GallonsLastMinute count
//if (secondCounter>=60)
//{
// secondCounter=0;
// tempstr += " GLM:";
// float GLM = ((float)(PulseCounter - PULSECOUNTLASTMINUTEMARK))/PulsesPerGAL;
// tempstr += (int)GLM;
// tempstr += '.';
// tempstr += ((int)(GLM * 100)) % 100;
// PULSECOUNTLASTMINUTEMARK = PulseCounter;
// EEPROM_Write_Counter(PulseCounter);
//}
tempstr.toCharArray(sendBuf, MAX_DATA_LEN);
sendLen = tempstr.length();
radio.send(GATEWAYID, sendBuf, sendLen);
radio.sleep();
DEBUGln(tempstr);
}
void loop() {
//void pulseCounter(void)
//if (debounce == 1) BEFORE = millis();
buttonState = digitalRead(INPUTPIN);
if (buttonState == LOW)
{debounce = 0; previous = 0;}
if (buttonState == HIGH)
{
if (debounce <= 30000) debounce++;
if ((debounce >= 4000) && (previous == 0))
{
debounce = 0;
noInterrupts();
previous = 1;
//DEBUGln(PulseCounterVolatile)
//delay(100000);
ledState = !ledState;
PulseCounterVolatile++; // increase when LED turns on
digitalWrite(LED, ledState);
NOW = millis();
//DEBUG(NOW - BEFORE)
//DEBUGln("ms")
EEPROM_Write_Counter(PulseCounterVolatile);
//remember how long between pulses (sliding window)
TIMESTAMP_pulse_prev = TIMESTAMP_pulse_curr;
TIMESTAMP_pulse_curr = NOW;
if (TIMESTAMP_pulse_curr - TIMESTAMP_pulse_prev > GPMthreshold)
//more than 'GPMthreshold' seconds passed since last pulse... resetting GPM
pulsesPerXMITperiod=pulseAVGInterval=0;
else
{
pulsesPerXMITperiod++;
pulseAVGInterval += TIMESTAMP_pulse_curr - TIMESTAMP_pulse_prev;
}
interrupts();
}
}
}
unsigned long EEPROM_Read_Counter()
{
return EEPROM_Read_ULong(EEPROM_Read_ULong(COUNTERADDR));
}
void EEPROM_Write_Counter(unsigned long counterNow)
{
if (counterNow == EEPROM_Read_Counter())
{
DEBUG("{EEPROM-SKIP(no changes)}");
return; //skip if nothing changed
}
DEBUG("{EEPROM-SAVE(");
DEBUG(EEPROM_Read_ULong(COUNTERADDR));
DEBUG(")=");
DEBUG(PulseCounter);
DEBUGln("}");
unsigned long CounterAddr = EEPROM_Read_ULong(COUNTERADDR);
if (CounterAddr == COUNTERADDRBASE+8*(COUNTEREEPROMSLOTS-1))
CounterAddr = COUNTERADDRBASE;
else CounterAddr += 8;
EEPROM_Write_ULong(CounterAddr, counterNow);
EEPROM_Write_ULong(COUNTERADDR, CounterAddr);
}
unsigned long EEPROM_Read_ULong(int address)
{
unsigned long temp;
for (byte i=0; i<8; i++)
temp = (temp << 8) + EEPROM.read(address++);
return temp;
}
void EEPROM_Write_ULong(int address, unsigned long data)
{
for (byte i=0; i<8; i++)
{
EEPROM.write(address+7-i, data);
data = data >> 8;
}
} The readout on gateway is now like this
[22] 3449 [RX_RSSI:-58]
With the following python code I am recording all changes made every 10L and record that to MYSQL database.
import time, sys, serial, threading
import collections
import httplib
import string
import smtplib
import re
# Import connection to database
from connection_to_database import *
### GENERAL SETTINGS ###
SERIALPORT = "/dev/ttyUSB0" # the default com/serial port the receiver is connected to
BAUDRATE = 115200
try:
#print "Tryign....!",SERIALPORT
ser = serial.Serial(SERIALPORT, BAUDRATE, timeout=30)
except:
print "Failed to connect on",SERIALPORT
while True:
line = ser.readline() # read a '\n' terminated line
line2 = line.replace("RX_RSSI:", '')
line3 = line2.split (' ')
line4 = [x.strip("[]\r\n") for i, x in enumerate(line3) if i<2]
node = line4[0]
m3=float(line4[1])/100
try:
c.execute("INSERT IGNORE INTO sensor_readout (sensor_id, volume) VALUES ('{0}', '{1}')".format(node, m3))
conn.commit() #commit the insert
except MySQLdb.IntegrityError:
print "failed to insert data"
Now every 26th (with another python code) in a month I get email notification on usage in one month.
New project:
No my friend ask me to build same/similar system for her and the thing is she has water meter for cold and hot water side by side and I was thinking how to use only one moteino and get same output reading result (if possible)
The idea is that when DIG PIN 4 would have interrupt it would it would change on
[24] 3456 [RX_RSSI:-58]
and if on DIG PIN 3 there would be a change on
[23] 4564 [RX_RSSI:-58]
So now the question what would be more easy adding additional readout as I imagined or creating two readout on one node ID (eg.) [23] 3456 4565 [RX_RSSI:-58]) and separate that with python. If I am honest I would try to avoid rewriting the python code. But of course I am open to any suggestion just to get thing working.
Tnx in advance
You could just do something like: [23] C:3456 H:4564 [RX_RSSI:-58] (cold, hot)
D3 is the only extra hardware interrupt available. For D4 you need to use pin change interrupts. See this for details: http://playground.arduino.cc/Main/PinChangeInt
Exactly. Add metadata to your reporting, and parse it using the python. I can fix your python if you decide on a reporting format.
C
Here you go. Here is my message processing on a node to generate JSON-formatted messages for an IO message:
void sendIOMessage(byte pin, byte mode, float value) {
int sendlength = 61; // default
if (mode == 0 || mode == 1 ) { // for integer values
sendlength = 31;
char sendstring[sendlength];
sprintf(sendstring, "iopin:%02d,iomode:%02d,iovalue:%04d", pin, mode, value);
sendWithSerialNotify(GATEWAYID, sendstring, sendlength);
}
else if (mode == 2){ // for float values
int wholePart = value;
long fractPart = (value - wholePart) * 10000;
sendlength = 34;
char sendstring[sendlength];
sprintf(sendstring, "iopin:%02d,iomode:%02d,ioval:%03d.%04d", pin,mode,wholePart, fractPart);
sendWithSerialNotify(GATEWAYID, sendstring, sendlength);
}
}
void sendWithSerialNotify(byte destination, char* sendstring, byte sendlength) {
Serial.print(F("SENDING TO "));
Serial.println(destination);
Serial.println(sendstring);
radio.sendWithRetry(destination, sendstring, sendlength);
Serial.println(F("SEND COMPLETE"));
}
And here is how it is processed on the node:
def processserialdata(data):
from cupid.pilib import parseoptions
datadicts = []
messages = []
# Break into chunks
split1 = data.strip().split('BEGIN RECEIVED')
for split in split1:
if split.find('END RECEIVED') >= 0:
message = split.split('END RECEIVED')[0]
messages.append(message)
try:
datadict = parseoptions(message)
except:
print('error parsing message')
else:
datadicts.append(datadict)
return datadicts, messages
The JSON-parser helper function is here:
def parseoptions(optionstring):
list = optionstring.split(',')
optionsdict={}
for item in list:
split = item.split(':')
optionsdict[split[0].strip()] = split[1].strip()
return optionsdict
And, if you're interested, although I use sqlite instead of MYSQL, I put it into a database like so:
def processremotedata(datadict, stringmessage):
import cupid.pilib as pilib
if 'nodeid' in datadict:
# We are going to search for keywords. Message type will not be explicitly declared so
# as not to waste precious message space in transmission. Or we could tack these on in
# the gateway, but we won't yet.
# Then we have to construct a query where we will replace a unique item
# This will take the form :
# update or replace in remotes where nodeid=3 and msgtype='iovalue' and iopin=3
# update or repalce in remotes where nodeid=2 and msgtype='owdev' and owrom='28XXXXXXXXXXXXXX'
# (and later which IO on this device)
# update or replace in remotes where nodeid=2 and msgtype='chanstat' channum=1
# (need to see if all channel variables can be fit into one message:
# channum, sv,pv,mode,state
runquery = False
nodeid = datadict['nodeid']
querylist = []
if 'iovalue' in datadict:
# check to see if entry exists with node and ionum. Need to generalize these.
# Might make sense to put then into an ID to compare. Other database, compatible?
# iovalue type message
try:
msgtype = 'iovalue'
keyvalue = datadict['iopin']
keyvaluename = 'iopin'
except:
print('oops')
else:
runquery = True
elif 'owdev' in datadict:
try:
msgtype = 'owdev'
keyvalue = datadict['owrom'][2:]
keyvaluename = 'owrom'
except:
print('oops')
else:
runquery = True
if runquery:
deletequery = pilib.makedeletesinglevaluequery('remotes', {'conditionnames': ['nodeid', 'keyvalue', 'keyvaluename'], 'conditionvalues': [nodeid ,keyvalue, keyvaluename]})
insertquery = pilib.makesqliteinsert('remotes', [nodeid, msgtype, keyvaluename, keyvalue, stringmessage, pilib.gettimestring()], ['nodeid', 'msgtype', 'keyvaluename', 'keyvalue', 'data', 'time'])
querylist.append(deletequery)
querylist.append(insertquery)
pilib.sqlitemultquery(pilib.controldatabase, querylist)
return 'all done'
else:
print('not running query')
All of this, most under mote, is available here: https://github.com/iinnovations/iicontrollibs
C