LowPowerLab Forum

Hardware support => Moteino => Topic started by: zigam on November 01, 2014, 10:29:32 AM

Title: Two imputs two ID one Node
Post by: zigam on November 01, 2014, 10:29:32 AM
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
Title: Re: Two imputs two ID one Node
Post by: Felix on November 01, 2014, 11:51:15 AM
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)
Title: Re: Two imputs two ID one Node
Post by: zigam on November 02, 2014, 08:25:49 AM
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 



 


Title: Re: Two imputs two ID one Node
Post by: Felix on November 02, 2014, 02:44:47 PM
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
Title: Re: Two imputs two ID one Node
Post by: ColinR on November 03, 2014, 09:51:59 AM
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
Title: Re: Two imputs two ID one Node
Post by: ColinR on November 03, 2014, 12:50:23 PM
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