0
votes

I have 3 Arduino-sensor nodes connected to a PC running Python, with XBee Series 1 radios as tools for wireless communications. The baud rate is set at 9600, and all address (ATDL, ATDH, ATMY) are set correctly as all the Arduino-sensor nodes are able to send data correctly to the XBee coordinator connected to my PC running on Python. The Xbee radios connected to the respective Arduinos (there are 2 Arduino Unos and 1 Arduino Nano) are configured as End Devices.

I recently found a problem in that any changes effected at the Arduino will lag by 5 seconds when it reaches the PC, and written to a CSV file. For example, I am reading the state of a pin on one of the Arduinos, and writing this state to a CSV file after it has been transmitted via XBee to my computer. However, I realized that when I effected the state change at 08:30:30 (HH:MM:SS), the change was only reflected in the CSV file at 08:30:35 (HH:MM:SS).

May I ask why is this the case and how should I resolve it? I have the following codes for Arduino and Python respectively.

Arduino (these codes are roughly the same across the 3 Arduino nodes):

#include <SoftwareSerial.h>
#define IR 10 // IR sensor at D10 position

#define pirPin 9 // Input for HC-S501
#define LEDPinPIR 12 // LED at Pin 12 (PIR)
#define lightLED 11 // LED at Pin 11 (Relay, Neg.Logic - ON = Relay off)

SoftwareSerial xbee(2, 3); // RX, TX

int pirValue; // Place to store read PIR Value
int pirNum = 0;
int pirNumyes = 0;
int pirNumno = 0;

int sw_door = 0; //sw_door has been updated to "sw_relay" w.e.f 27-Feb-2018
int IR_val = 0;

char incomingByte;
unsigned long prevMillis = 0;

void setup() {  
  Serial.begin(9600);
  xbee.begin(9600);
  pinMode(pirPin, INPUT); // PIR sensor
  pinMode(LEDPinPIR, OUTPUT); // Ultrasound sensor indicator
  pinMode(lightLED, OUTPUT); // LED at Pin 11 (Relay, Neg.Logic - ON = Relay off)
  pinMode(SW, INPUT); // Switch
  digitalWrite(SW, LOW);
  digitalWrite(LEDPinPIR, LOW);
}

void loop() {

  unsigned long currentMillis = millis();

  if((unsigned long)currentMillis - prevMillis == 1000){

    //IR sensor "d" refers to door

    if (digitalRead(IR) == LOW){
      IR_val = 1;
      String ID = "d";
      String IRID = ID + IR_val;
      Serial.print(IRID);
      Serial.print(',');
      xbee.print(IRID);
      xbee.print(',');
    }
    else{
      IR_val = 0;
      String ID = "d";
      String IRID = ID + IR_val;
      Serial.print(IRID);
      Serial.print(',');
      xbee.print(IRID);
      xbee.print(',');
    }

    // Motion sensor
    pirValue = digitalRead(pirPin);
    if (pirValue == HIGH) {
      pirNumyes = 1;
      Serial.print(pirNumyes);
      Serial.print(',');
      xbee.print(pirNumyes);
      xbee.print(',');
      digitalWrite(LEDPinPIR, HIGH);
    }
    else {
      pirNumno = 0;
      Serial.print(pirNumno);
      Serial.print(',');
      xbee.print(pirNumno);
      xbee.print(',');
      digitalWrite(LEDPinPIR, LOW);
    }

    // Switch 
    if(digitalRead(lightLED)== HIGH){
      sw_door = 0;
      Serial.print(sw_door);
      Serial.println(',');
      xbee.print(sw_door);
      xbee.println(',');
    }
    else{
      sw_door = 1;
      Serial.print(sw_door);
      Serial.println(',');
      xbee.print(sw_door);
      xbee.println(',');
    }
    prevMillis = currentMillis;
  }

  // Xbee to Arduino Added: 18-Feb-2018
  if (xbee.available()){
    incomingByte = xbee.read();

    if(incomingByte == '1'){
      digitalWrite(lightLED, HIGH);
      //xbee.println("OK");
    }
    else if(incomingByte == '0'){
      digitalWrite(lightLED, LOW);
      //xbee.println("OK");
    }
  }
}

Python:

import threading
import time
import serial
import csv

# Arduino; Arduino is now replaced by XBee modules
arduino = serial.Serial('COM18', 9600, timeout=1)  # Open serial port.

def acquire_data():
    while True:
        try:
            data_in = arduino.readline()  # read serial data from Arduino
        except:
            pass

        data_stripped = data_in.strip()  # Removes spaces and \n

        for data_stripped in arduino:
            if data_stripped.startswith('b') and data_stripped.count(
                    ',') == 3:  # first char identifies where data is coming from; count commas to double-check incoming string
                field = data_stripped.split(',')  # split data to be put into 'boxes'
                bed_sen = field[0] + ',' + field[1] + ',' + field[2]  # We have 3 data sensor fields
                bed_sen_fill = True  # A flag to show that this if-statement has been completed

            if data_stripped.startswith('t') and data_stripped.count(',') == 3:
                field = data_stripped.split(',')
                table_sen = field[0] + ',' + field[1] + ',' + field[2]
                table_sen_fill = True

            if data_stripped.startswith('d') and data_stripped.count(',') == 3:
                field = data_stripped.split(',')
                door_sen = field[0] + ',' + field[1] + ',' + field[2]
                door_sen_fill = True

            try:
                if bed_sen_fill == True and table_sen_fill == True and door_sen_fill == True:
                    data_combi = bed_sen + ',' + table_sen + ',' + door_sen
                    break
            except:
                pass

        if data_combi:
            datasplit = data_combi.split(",")
            field1 = datasplit[0]
            field2 = datasplit[1]
            field3 = datasplit[2]
            field4 = datasplit[3]
            field5 = datasplit[4]
            field6 = datasplit[5]
            field7 = datasplit[6]
            field8 = datasplit[7]
            field9 = datasplit[8]

        with open('abs_testing.csv', 'ab') as csvfile:  # 'ab' to remove newline char after each print
            writer = csv.writer(csvfile)
            sensor_fields = [field1, field2, field3, field4, field5, field6, field7, field8, field9,
                             time.strftime("%H%M%S")]
            writer.writerow(sensor_fields)

        time.sleep(1)

def counting():
     while True:
         sum = 3 + 2
         sum2 = sum*8
         print sum2
         time.sleep(0.2)

def on_light():
    strin = '1'
    arduino.write(strin.encode())
    print "Confirm ON"

def off_light():
    strin = '0'
    arduino.write(strin.encode())
    print "Confirm OFF"

# now threading1 runs regardless of user input
threading1 = threading.Thread(target = acquire_data)
threading2 = threading.Thread(target = counting)
threading1.daemon = False # May remove later. Unsure at the moment.
threading2.daemon = False # May remove later. Unsure at the moment.
threading1.start()
threading2.start()

while True:
    if raw_input() == 't':
        on_light()
        print "ON"
    if raw_input() == 'r':
        off_light()
        print "OFF"

    time.sleep(1)

Multithreading is implemented here by a silly operation that finds what is 8*5, because later, this will be expanded to a real-time machine learning function that determines when the lights should turn on/off. The raw_input() functions are proofs that data can be relayed back to the Arduino-sensor node.

Thank you so much for your help! :)

1
Try something like writer.sync() (I do not know if there is this func call in the API, but you can try to find something similar) right after you call the writer.writerow() to see if the problem persists. Maybe it could be something with buffered IO. - campescassiano
@phyloflash Thank you so much for your suggestion! ^^ I will read more about the function and then try it~ ^^ - galaxy_twirl

1 Answers

0
votes

A few thoughts on possible improvements:

  • Increase Serial() baud rate to 115200 on Arduino.
  • Increase XBee baud rate to 115200 on Arduino (or as high as possible with software serial).
  • Increase baud rate to 115200 on PC running Python (it can definitely handle that rate, and the rates on each device don't need to match).
  • Remove or reduce the sleep(1) in acquire_data().
  • Check the sleep configuration on the end devices. Is it possible they're sleeping for seconds at a time? Can you configure them as routers to eliminate that as a possible cause of delays?
  • Change your sending code to create a single string (see below) and then send it. Then you could easily send it to the XBee first and the Serial output separately. If the Serial() interface isn't buffered, you could be timing your XBee packet out (see ATRO setting) and having your response sent as multiple packets (which is less efficient). Try increasing ATRO from its default of 3 to 20 and see if that helps.
  • Right now you send every second. What if you checked I/O more frequently and only sent responses when an input changed (or it had been 5 seconds since the last transmission)?

Your sending every second, so on average you'll send 0.5 seconds after an I/O change. You could add some timing code on the Arduino to print out how many milliseconds it takes to assemble and send your data (which could account for some of the delay). Or try this replacement code:

char buffer[16];

pirValue = digitalRead(pirPin);
digitalWrite(LEDPirPIN, pinValue);

sprintf(buffer, "d%u,%u,%u,\n",
    digitalRead(IR) == LOW,
    pirValue == HIGH,
    digitalRead(lightLED) == LOW);
xbee.print(buffer);
Serial.print(buffer);