I am trying to get an Arduino to give data to Python form a sensor. Then when this data is received into Python, Python needs to send a signal back to the Arduino to get a motor to react to the incoming data. Presently my code results in this, however if I look at the data I will have random spikes of data that do not coincide with the scenario. For example I will place a 5 newton load on the sensor and it will read 5 newtons for several iterations, then it will spike to something that is random. I have looked at just the Arduino output in the Arduino IDE and it is a steady and constant stream of data. I have looked at what Python receives without having the out going signal and that gives the same thing as the Arduino. I don't know why when I have to two send information back and forth I get this issue. If anyone has a better solution on how to get good readings I would appreciate it.
Python
import serial
import csv
import time
import sys
from time import localtime, strftime
import warnings
import serial.tools.list_ports
__author__ = 'Matt Munn'
arduino_ports = [
p.device
for p in serial.tools.list_ports.comports()
if 'Arduino' in p.description
]
if not arduino_ports:
raise IOError("No Arduino found - is it plugged in? If so, restart computer.")
if len(arduino_ports) > 1:
warnings.warn('Multiple Arduinos found - using the first')
Arduino = serial.Serial(arduino_ports[0],9600,timeout=1)
time.sleep(2)
start_time=time.time()
Force = []
Actuator_Signal=[]
Cycle_Count=[]
state = True
Up = True
numPoints = 10
ForceList = [0]*numPoints
AvgForce = 0
#This allows the user to input test conditions.
Force_Input = input("What is the force you want to test with in Newtons?")
Cycles = input("How many cycles would you like to run?")
Material = input("What kind of material is to be tested?")
print("The test will be conducted with", Force_Input, " Newtons of force and for a total of" , Cycles, " cycles.", " on", Material)
print ("REMOVE HANDS AND OTHER OBJECTS AWAY FROM AREA OF OPERATION.")
print("Testing will begin.")
time.sleep(5)
start_time = time.time()
#This creates the unique file for saving test result data.
outputFileName = " Cycle_Pull_Test_#.csv"
outputFileName = outputFileName.replace("#", strftime("%Y-%m-%d_%H %M %S", localtime()))
with open(outputFileName, 'w',newline='') as outfile:
HeaderNames = ['Material','Newtons','Time']
outfileWrite = csv.DictWriter(outfile, fieldnames = HeaderNames)
outfileWrite.writeheader()
#This takes the data from the arduino and interprits it.
while True:
while (Arduino.inWaiting()==0):
pass
try:
data = Arduino.readline()
dataarray = data.decode().rstrip().split(',')
for i in range(0,numPoints):
Force = abs(round(float(dataarray[0]),3))
ForceList[i] = Force
AvgForce = round((sum(ForceList)/numPoints),3)
elapsed_time = round(time.time()-start_time,3)
print (AvgForce, elapsed_time)
#This Controls the actuators direction based on the force input on the loadcell.
if AvgForce > float(Force_Input):
Up = False
state = True
Arduino.write(b'd')
else:
Arduino.write(b'u')
Up = True
if state == True and Up == True:
state = False
Cycles = float(Cycles) + 1
if Cycles >= float(Cycle_Count[0]):
Arduino.write(b'o')
print("Testing is done.")
time.sleep(1)
sys.exit("All the data will be saved now")
except (KeyboardInterrupt, SystemExit,IndexError,ValueError):
pass
#This writes the data from the loadcell to a csv file for future use.
outfileWrite.writerow({'Material' : Material, 'Newtons' : AvgForce, 'Time' :elapsed_time })
Arduino
#include <HX711_ADC.h> // HX711 Library
#include "CytronMotorDriver.h"
// Configure the motor driver.
CytronMD motor(PWM_DIR, 3, 2); // PWM = Pin 3, DIR = Pin 2.
int up = HIGH;
int down = LOW;
int dstate = up;
int python_direction = 0;
float interval = 12000;
float pretime= 0;
float curtime = 0;
HX711_ADC LoadCell(11, 12);
float force;
float calforce;
float newtons;
void setup() {
Serial.begin(9600);
LoadCell.begin();
LoadCell.start(2000); // tare preciscion can be improved by adding a few seconds of stabilising time
LoadCell.setCalFactor(100.0); // user set calibration factor (float)
}
void loop() {
LoadCell.update();
forceRead();
actuatorControl();
}void forceRead()
{
force = LoadCell.getData();
force = (force/285); // Force in (N) // 285 is conversion factor
calforce = (-1.0389*force)+0.0181, // This is in lbs
newtons = 4.45*calforce;
Serial.println(newtons);
//receive from serial terminal for tare
if (Serial.available() > 0) {
char inByte = Serial.read();
if (inByte == 't') LoadCell.tareNoDelay();
}
}
void actuatorControl(){
if (Serial.available()>0){
python_direction = Serial.read();
Serial.print (python_direction);
Serial.print (",");
}
if (python_direction == 'd'){
motor.setSpeed(255); // Run forward at full speed.
}
if (python_direction == 0){
motor.setSpeed(0); // Stop motor.
}
if (python_direction == 'u'){
motor.setSpeed(-255); // Run backward at full speed.
}
}
Related
If I make a loop on my Raspberry Pi from 1 to 10 and assigned to a variable x for a small example, how do I take it and transfer it to an Arduino via Serial to be able to be used for an angle for my stepper motor or to simply make it usable as a variable in a loop?
Is there a small code from a Pi and Arduino each that can help me? I know this is like an easy thing, but I'm trying to find a reference because I'm expanding upon this, using Node Red, Stepper Motors, Water Valves, and a ton of other things.
Are you talking about general serial communication? I have something that will work on both ends. It is not simple
Here is what you should run on the Pi.
Change Baud rate to proper rate for your device
Change "Possible_Parameters" to a list of possible angles to run
import time
import serial
import numpy as np
import serial.tools.list_ports
from serial.tools.list_ports import comports
import sys
import glob
import serial
def serial_ports():
""" Lists serial port names
:raises EnvironmentError:
On unsupported or unknown platforms
:returns:
A list of the serial ports available on the system
"""
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or
sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
ports = glob.glob('/dev/tty[A-Za-z]*')
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
for i in range(len(serial_ports())):
print(i, serial_ports()[i])
if len(serial_ports()) > 0:
Port_Selected = int(input("Select Port like 0: "))
Port = serial_ports()[Port_Selected]
Baud = 9600
X=1
else:
print("No ports detected")
X=0
pass
if X==1:
with serial.Serial(Port, Baud, timeout=.1) as Qtpy:
if Qtpy.isOpen():
print('{} connected'.format(Qtpy.port))
try:
while True:
if X==1:
Possible_Parameters=["List", "Keys", "Here"]
for i in range(len(Possible_Parameters)):
print(i, Possible_Parameters[i])
Possible_Parameter_Choice = int(input("Choose Parameter to change :"))
Msg1 = Possible_Parameters[Possible_Parameter_Choice]
Msg_1=Msg1 + '\n' #add ending parameter for C++ serial communication
Msg2 = (input("Input a new value for parameter: "))
Msg_2=Msg2 + '\n'
#print (Msg_B)
Qtpy.write(Msg_1.encode())
Qtpy.write(Msg_2.encode())
X=0
while Qtpy.inWaiting() == 0:
pass
if Qtpy.inWaiting() > 0:
Msg_Back = Qtpy.readline()
print (Msg_Back)
#Qtpy.flushInput()
#X = input("Set X to 1")
#time.sleep(0.02)
except KeyboardInterrupt:
print('Keyboard interrupted')
Here is something for your arduino. Notice that I am using pairs. I do this to make one a key for the value. The first item received identifies where the value will go. Note: I did cannibalize my code for the arduino part so you will need to double check it for errors
// Prep Serial Communication Variables - 7 variables
const uint8_t Max_Length = 32;
const uint8_t Max_Value_Length = 16;
char MSG_In[Max_Length]; //Parameter name (Send in pairs with value
char MSG_In_Value[Max_Value_Length]; //Parameter value
char MSG_Out[Max_Length];
char Bits_N_Pieces; // bytes recieved
bool Incoming; // Gets set to 1 when serial communication is recieved
char Key[] = Your_Keyword;
// Convert you angles to integers before sending
int Value;
// Or use this and change atoi in later statement
// Library to convert float to char
/* #include <avr/dtostrf.h>
float Value; */
void setup() {
Serial.begin(9600);
}
void loop() {
// Serial Communication
while (Serial.available() > 0) {
Incoming=1;
static unsigned int MSG_Position = 0;
// read the incoming byte:
Bits_N_Pieces = Serial.read();
//Serial.println(Bits_N_Pieces);
if (Bits_N_Pieces != '\n' && MSG_Position < Max_Length -1) {
MSG_In[MSG_Position] = Bits_N_Pieces;
// Serial.println(MSG_In);
MSG_Position++;
} else {
MSG_In[MSG_Position] = '\0';
MSG_Position = 0;
break;
}
}
if (Incoming==1) {
Incoming=0;
while (Serial.available() > 0) {
// Serial.println("Reading Value");
static unsigned int MSG_Position = 0;
Bits_N_Pieces = Serial.read();
if(Bits_N_Pieces != '\n' && MSG_Position < Max_Value_Length -1) {
MSG_In_Value[MSG_Position] = Bits_N_Pieces;
//Serial.println(MSG_In_Value);
MSG_Position++;
} else {
MSG_In_Value[MSG_Position] = '\0';
MSG_Position =0;
break;
}
}
if (strcmp(MSG_In, Key) == 0) {
Value = atoi(MSG_In_Value);
/* Value = atof(MSG_In_Value);*/
}
//Zero out MSG_XXX to prep for next communication
bzero(MSG_In, Max_Length);
bzero(MSG_In_Value, Max_Value_Length);
}
delay(1000);
}
This is my arduino code
I closed my arduino ide while running the python code, learned from past mistakes that the com port can only be accessed by one application at a time
int analogPin = A0;
int ledPin = 13;
int val = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
int ledPin = 13;
}
void loop() {
// put your main code here, to run repeatedly:
if( millis() % 20 != 0 ) // so a reading is taken every 2 milliseconds
return;
// turn the ledPin on
digitalWrite(ledPin, HIGH);
val = analogRead(analogPin); // read the input pin
Serial.println(val); // debug value
// turn the ledPin off:
digitalWrite(ledPin, LOW);
}
it works correctly and give me readings every fixed interval
and could be graphed or viewed in the arduino ide integrated serial monitor
This is my python code
import time
import logging
import threading
import numpy as np
import cv2
import serial
# configure serial for reading from arduino
# set baudrate and port name accordingly
COM = 'COM6'
baudRate = 9600
sensorDataFileName = r"C:\Users\cy316\Documents\FYP\SensorData.txt"
videoCaptureName = "CameraCapture.mp4"
captureTime = 10
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
ser = serial.Serial(COM, baudRate)
cap = cv2.VideoCapture(0) # 0 for video camera
time.sleep(10) #allow time for arduino to reset
# function for writing the arduino readings into a file
def sensorreading(ser, stoptime):
sensordatafile = open(sensorDataFileName,"w+")
starttime = time.time()
data = []
while ((starttime-time.time())%0.02 == 0): # run every 20ms, better than delay which might or might not be on time
b = ser.readline()
string_n = b.decode()
string = string_n.rstrip()
flt = float(string)
print(flt)
data.append(flt) # add to the end of data list
time.sleep(0.02) # wait for arduino to take new reading
if (starttime-time.time()>stoptime): # if i reach stoptime, return
sensordatafile.close()
return
# function for video capture
def videocapture(cap, stoptime):
starttime = time.time()
# check if camera is available
if (cap.isOpened() == False):
print("Unable to read camera feed")
return
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
out = cv2.VideoWriter(videoCaptureName,fourcc, 30, (frame_height,frame_width))
cv2.imshow('Frame',frame)
while(starttime-time.time()<stoptime):
ret, frame = cap.read()
out.write(frame)
out.release()
return
# testcell
sensorreading(ser,captureTime)
returned only one value, once, and not my specified timing
also failed to write it into the txt file
# The cell i want to run for simultaneous sensor and camera values
SensorThread = threading.Thread(target=sensorreading(ser,captureTime), args=(1,))
VideoThread = threading.Thread(videocapture(cap,captureTime), args=(1,))
SensorThread.start()
VideoThread.start()
is it because i am running this off jupyter notebook and not something like pycharm?
or is it something to do with the serial code i am missing
I am developing a code that is suppose to take data from a load cell and based on that data it should control a motor. If the load cell reads a specific value the motor should rotate one direction, if it reads a different value it should go in the other direction. Right now if I apply a load to the sensor the motor will go in one direction, when the load is removed the motor will not go the other way. Instead it continues to go in the wrong direction. I am also having python save the data from the load cell to a csv file and because of that I have noticed that the load cell value stays at a high number like 117 newtons even when the force from the load cell is removed. Before having the communication between arduino and python the output of the sensor was spot on. I have read a little on duplexing and was thinking maybe there in lies my problem. Any help on why my python force value is wrong now would be great.
I have tried a different format to making my arduino code. Arduino Code 2 works, better, I can get the motor to respond to the inputs on the load cell, however as I print the data coming into python it does get a little screwed up. This does not affect my motor surprisingly though. I intend on keeping this incoming data for future use and was hoping that it could be cleaned up. For example lets say that I have 5 newtons of force on there it will display 5 newtons for a few seconds then it will spike to a random number for a couple instances then back to normal.
Python
import serial
import csv
import time
from time import localtime, strftime
import warnings
import serial.tools.list_ports
__author__ = 'Matt Munn'
arduino_ports = [
p.device
for p in serial.tools.list_ports.comports()
if 'Arduino' in p.description
]
if not arduino_ports:
raise IOError("No Arduino found - is it plugged in? If so, restart computer.")
if len(arduino_ports) > 1:
warnings.warn('Multiple Arduinos found - using the first')
Arduino = serial.Serial(arduino_ports[0],9600,timeout=1)
time.sleep(2)
start_time=time.time()
Force = []
Actuator_Signal=[]
outputFileName = "Cycle_Pull_Test_#.csv"
outputFileName = outputFileName.replace("#", strftime("%Y-%m-%d_%H %M %S", localtime()))
with open(outputFileName, 'w',newline='') as outfile:
outfileWrite = csv.writer(outfile)
while True:
while (Arduino.inWaiting()==0):
pass
try:
data = Arduino.readline()
dataarray = data.decode().rstrip().split(',')
Force = round(float(dataarray[0]),3)
print (Force)
if Force < 50:
Arduino.write(b'u')
print ('up')
else:
Arduino.write(b'd')
print('down')
except (KeyboardInterrupt, SystemExit,IndexError,ValueError):
pass
outfileWrite.writerow([Force,"N"])
Arduino
#include <HX711_ADC.h>
#include "CytronMotorDriver.h"
// Configure the motor driver.
CytronMD motor(PWM_DIR, 3, 2); // PWM = Pin 3, DIR = Pin 2.
int up = HIGH;
int down = LOW;
int dstate = up;
int python_direction = 0;
float interval = 12000;
float pretime= 0;
float curtime = 0;
// LOAD CELL
//HX711 constructor (dout pin, sck pin)
HX711_ADC LoadCell(11, 12);
float force;
float calforce;
float newtons;
// The setup routine runs once when you press reset.
void setup() {
Serial.begin(9600);
LoadCell.begin();
LoadCell.start(2000); // tare preciscion can be improved by adding a few seconds of stabilising time
LoadCell.setCalFactor(100); // user set calibration factor (float)
}
// The loop routine runs over and over again forever.
void loop() {
if (Serial.available()>0){
python_direction = Serial.read();
Serial.print (python_direction);
Serial.print (",");
}
LoadCell.update();
force = LoadCell.getData();
force = (force/285); // Force in (N) // 285 is conversion factor
calforce = (-1.0389*force)+0.0181, // This is in lbs
newtons = 4.45*calforce;
receive from serial terminal for tare
if (Serial.available() > 0) {
char inByte = Serial.read();
if (inByte == 't') LoadCell.tareNoDelay();
}
unsigned long curtime = millis();
if (python_direction == 'u'){
motor.setSpeed(255); // Run forward at full speed.
}
if (python_direction == 0){
motor.setSpeed(0); // Stop motor.
}
if (python_direction == 'd'){
motor.setSpeed(-255); // Run backward at full speed.
}
Serial.println(newtons);
}
Arduino 2
#include <HX711_ADC.h> // HX711 Library
#include "CytronMotorDriver.h"
// Configure the motor driver.
CytronMD motor(PWM_DIR, 3, 2); // PWM = Pin 3, DIR = Pin 2.
int up = HIGH;
int down = LOW;
int dstate = up;
int python_direction = 0;
float interval = 12000;
float pretime= 0;
float curtime = 0;
HX711_ADC LoadCell(11, 12);
float force;
float calforce;
float newtons;
void setup() {
Serial.begin(9600);
LoadCell.begin();
LoadCell.start(2000); // tare preciscion can be improved by adding a few seconds of stabilising time
LoadCell.setCalFactor(100.0); // user set calibration factor (float)
}
void loop() {
LoadCell.update();
forceRead();
actuatorControl();
}void forceRead()
{
force = LoadCell.getData();
force = (force/285); // Force in (N) // 285 is conversion factor
calforce = (-1.0389*force)+0.0181, // This is in lbs
newtons = 4.45*calforce;
Serial.println(newtons);
//receive from serial terminal for tare
if (Serial.available() > 0) {
char inByte = Serial.read();
if (inByte == 't') LoadCell.tareNoDelay();
}
}
void actuatorControl(){
if (Serial.available()>0){
python_direction = Serial.read();
Serial.print (python_direction);
Serial.print (",");
}
if (python_direction == 'd'){
motor.setSpeed(255); // Run forward at full speed.
}
if (python_direction == 0){
motor.setSpeed(0); // Stop motor.
}
if (python_direction == 'u'){
motor.setSpeed(-255); // Run backward at full speed.
}
}
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! :)
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);
I'm getting a value of my moisture sensor over serial from Arduino to Raspberry PI. My Python script is supposed to log it. And it does, but the timing is getting delayed more and more exponentially. My guess (after 5 hours of Google) is that the problem is in the buffer somewhere and I am reading old data and "catching up". How do I adjust my code to get the latest serial info from my Arduino? Please keep in mind that I am a BIG noob and if you can explain as simple as possible. I've been up all night figuring it out but it's just that I know so very little about programming. Also adding the graph, there you can see the drift-off, I put the sensor out of water and put it in when I saw the graph drop.
Arduino code:
/*
Chirp - arduino example
Connection
Chirp pin 1 - no connection
Chirp pin 2 - Arduino VCC
Chirp pin 3 - Arduino A5
Chirp pin 4 - Arduino A4
Chirp pin 5 - Arduino pin 2
Chirp pin 6 - Arduino GND
*/
#include <Wire.h>
#define RELAY1 7
void writeI2CRegister8bit(int addr, int value) {
Wire.beginTransmission(addr);
Wire.write(value);
Wire.endTransmission();
}
unsigned int readI2CRegister16bit(int addr, int reg) {
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.endTransmission();
delay(1100);
Wire.requestFrom(addr, 2);
unsigned int t = Wire.read() << 8;
t = t | Wire.read();
return t;
}
void setup() {
Wire.begin();
Serial.begin(9600);
pinMode(RELAY1, OUTPUT);
digitalWrite(RELAY1,HIGH);
pinMode(2, OUTPUT);
digitalWrite(2, LOW); //Reset the Chirp
delay(1); //maybe allow some time for chirp to reset
digitalWrite(2, HIGH); //Go out from reset
writeI2CRegister8bit(0x20, 3); //send something on the I2C bus
delay(1000); //allow chirp to boot
}
void loop() {
Serial.println(readI2CRegister16bit(0x20, 0)); //read capacitance register
//writeI2CRegister8bit(0x20, 3); //request light measurement
//delay(9000); //this can take a while
//Serial.print(", ");
//Serial.println(readI2CRegister16bit(0x20, 4)); //read light register
if (readI2CRegister16bit(0x20, 0) < 420){
//Serial.println ("watering");
digitalWrite(RELAY1,LOW);
delay(2000);
digitalWrite(RELAY1,HIGH);
delay(2000);
}
else{
digitalWrite(RELAY1,HIGH);
//Serial.println ("moist");
delay(2000);
}
}
Python code:
import serial
import time
import csv
import os
import plotly.plotly as py
from plotly.graph_objs import Scatter, Layout, Figure
os.chdir('/home/pi/csvdata')
username = '------------'
api_key = '------------'
stream_token = '------------'
py.sign_in(username, api_key)
trace1 = Scatter(
x=[],
y=[],
stream=dict(
token=stream_token,
maxpoints=200000000000
)
)
layout = Layout(
title='------------'
)
fig = Figure(data=[trace1], layout=layout)
print py.plot(fig, filename='------------')
stream = py.Stream(stream_token)
stream.open()
def mainloop():
name=time.strftime('%b %d %Y %H:%M:%S')
f=open(name+'.csv', 'wt')
writer = csv.writer(f,delimiter=',')
ser = serial.Serial('/dev/ttyUSB0',9600,timeout=1)
for t in range(500):
#time.sleep(5)
kk=ser.readline()
kk2=kk.split('\r',1)
s=kk2[0]
text=time.strftime('%X %x'),s
streamtext=({'x': time.strftime('%X %x'), 'y': kk})
writer.writerow (text)
stream.write (streamtext)
f.flush()
print (text)
time.sleep(5);
ser.flush()
#ser.flushInput()
#ser.flushOutput()
if t == 499:
print ("why")
f.close()
ser.close()
mainloop()
else:
time.sleep(5);
mainloop()
You haven't posted your code from the Arduino and Python, so here is an example that demonstrates what you want to do. copy the relevant parts into your code.
Arduino:
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
randomSeed(analogRead(0));
}
void loop() {
// put your main code here, to run repeatedly:
int moisture_value = random(300);
// millis() is the time in milliseconds since the arduino started running
Serial.println(String(millis()) + ":" + String(moisture_value)); // We send two pieces of data, i.e. time since arduino started and the sensor value
delay(1000);
}
Python:
import serial
import datetime
# Converts to an integer if it is an integer, or it returns it as a string
def try_parse_int(s):
try:
return int(s)
except ValueError:
return s
ser = serial.Serial('/dev/ttyACM0', 115200)
time_start = datetime.datetime.now() # The time that we started receiving data
while True:
data = ser.readline().decode("utf-8").strip('\n').strip('\r') # remove newline and carriage return characters
[time, moisture_value] = data.split(':')
print("Received: '{}'".format(data))
time_received = time_start + datetime.timedelta(milliseconds=try_parse_int(time)) # Add delta time to start time
print("Moisture value: {} at {}".format(try_parse_int(moisture_value), time_received))
This works by sending the time since the Arduino started along with the sensor reading. If we add this to the know starting time in Python, then we know the time that the reading was taken. Even if the sending is delayed for some reason, it doesn't matter.
Example output:
Received: '0:116'
Moisture value: 116 at 2017-03-15 11:26:43.024711
Received: '1000:4'
Moisture value: 4 at 2017-03-15 11:26:44.024711
Received: '2000:128'
Moisture value: 128 at 2017-03-15 11:26:45.024711
Received: '3001:123'
Moisture value: 123 at 2017-03-15 11:26:46.025711
Time formatting:
You can also format the time in a way that you prefer;
time_received = time_start + datetime.timedelta(milliseconds=try_parse_int(time)) # Add delta time to start time
time_formatted = '{0:%H}:{0:%M}:{0:%S} on {0:%d}/{0:%m}/{0:%y}'.format(time_received)
print("Moisture value: {} at {}".format(try_parse_int(moisture_value), time_formatted))
Output:
Moisture value: 46 at 11:42:10 on 15/03/17
Received: '117050:174'
Moisture value: 174 at 11:42:11 on 15/03/17
Received: '118050:298'