I am trying to control a total of 6 LEDS with python. I was using pyserial to send arduino some data but I have encountered several problems with it.
The first problem I have encountered was:
According to the code I have written on arduino, the LEDS should blink for 1 second for several amount of times a specific data received. (This is later explained below.) However, the LEDS stay on the number of seconds they should blink. Meaning if the LEDS are supposed to blink for 10 times. The LEDS stay on for 10 seconds and turn off.
The second problem was:
The if conditions I have put in the code was not in order. As you can see in the arduino code, the if conditions are in order. However, this is what happens when I run the code.
The first LED lights up for 10 seconds, the second one lights up for 10 seconds as well. And later on the fifth one lights up.
To explain a little more of the code:
I am storing lists inside a list inside of python. There is a for loop that sends each list with a delay of 1 second.
The list has 6 elements.( This is for later experimentation.) However, in this work only the first two of elements of each list matter.
In order to negate the auto reset on arduino, I put 10 microfarad capacitor between the ground and reset. After that I run the python code to send the data.
I think I have explained the situation with details, however I am open to suggestions and will answer questions on the comments.
The Python Code:
import time
import serial
incomingByte2=[[1,20,200,300,400,500],[2,30,24,63,200],[3,5,400,500,100,200],[4,10,1,1,1,1],[5,10,1,1,1,1],[6,10,1,1,1,1]]
uzunluk= len(incomingByte2)
def close():
# arduino=serial.Serial("COM4",9600)
arduino = serial.Serial(
port='COM3',\
baudrate=115200,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print("connected to: " + arduino.portstr)
for i in range(0,uzunluk):
arduino.write(str.encode(str(incomingByte2[i])))
time.sleep(1)
The Arduino Code:
int ledPins[] = {2,3,4,5,6,7,8,9};
int incomingdata[6];
int ilkled,ikinciled,ucunculed,dordunculed,besinciled,altinciled;
void setup() {
// put your setup code here, to run once:
int index;
Serial.begin(115200);
for(index = 0; index <= 7; index++)
{
pinMode(ledPins[index],OUTPUT);
}
}
void loop() {
// put your main code here, to run repeatedly:
if(Serial.available()){
for (int a=0; a < 6; a++) {
incomingdata[a] = Serial.parseInt();
delay(100);
ilkled=incomingdata[0];
ikinciled=incomingdata[1];
ucunculed=incomingdata[2];
dordunculed=incomingdata[3];
besinciled=incomingdata[4];
altinciled=incomingdata[5];
}
}
if (ilkled==1){
for (int x=0;x<ikinciled;x++){
digitalWrite(ledPins[0],HIGH);
delay(1000);
digitalWrite(ledPins[0],LOW);
}
}
if (ilkled==2){
for (int x=0;x<ikinciled;x++){
digitalWrite(ledPins[1],HIGH);
delay(1000);
digitalWrite(ledPins[1],LOW);
}
}
if (ilkled==3){
for (int x=0;x<ikinciled;x++){
digitalWrite(ledPins[2],HIGH);
delay(1000);
digitalWrite(ledPins[2],LOW);
}
}
if (ilkled==4){
for (int x=0;x<ikinciled;x++){
digitalWrite(ledPins[3],HIGH);
delay(1000);
digitalWrite(ledPins[3],LOW);
}
}
if (ilkled==5){
for (int x=0;x<ikinciled;x++){
digitalWrite(ledPins[4],HIGH);
delay(1000);
digitalWrite(ledPins[4],LOW);
}
}
if (ilkled==6){
for (int x=0;x<ikinciled;x++){
digitalWrite(ledPins[5],HIGH);
delay(1000);
digitalWrite(ledPins[5],LOW);
}
}
}
I think your read loop is broken. It should close after delay(100), no?
for (int a=0; a < 6; a++) {
incomingdata[a] = Serial.parseInt();
delay(100);
}
Personally I would not string encode the data in Python. Send it as raw bytes, then read it as raw bytes into your int array.
Serial.readBytes( incomingData, 6 ); // assumes 8 bit ints.
That would do away with the loop completely.
Your LED staying on instead of flashing because you missed the line I added below.
for (int x=0;x<ikinciled;x++){
digitalWrite(ledPins[5],HIGH);
delay(1000);
digitalWrite(ledPins[5],LOW);
delay(1000); // <<<< Hold the LOW time
}
Otherwise it will be set LOW for only a few microseconds.
You may also run into synchronous issues with your Serial read versus how much time you spend in "delay()" during the LED flashing. Your python looks like it's sleeping only for 1 second, but your code responding to that will take many seconds as it delays in delay().
The Serial buffer will overflow and data will be lost/overwritten and when you call your next "parseInt" or "readBytes" there is no guarantee where the next bit of data in the buffer starts. Highly likely not at the next block of 6 ints.
You could send the data less often or send it based on how long the flashing will take. Alternatively you could implement an interrupt system to flash the LEDs... and the solutions get more complex from there up.
Welcome to the world of low level communications protocols.
PS, get rid of these
if (ilkled==6){
Just use it directly.
digitalWrite(ledPins[ilkled-1],HIGH);
Related
I am fairly new to hardware. I want to control an LED light using NodeMCU and Python. I uploaded an Arduino code in nodeMCU and then used pyserial library to get the serial output. But when I try to give input to the port, it doesn't work. I don't know where the problem is.
Here is the arduino code:
int inputVal = 0;
const int ledPin = 5; //D1 pin of NodeMCU
void setup() {
Serial.begin(9600);
delay(100);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, 0);
}
void loop() {
while(Serial.available()>0){
inputVal = Serial.read();
}
Serial.println(inputVal);
if(inputVal==1){
digitalWrite(ledPin, HIGH);
Serial.println("LED is ON");
}
else{
digitalWrite(ledPin, LOW);
Serial.println("LED is OFF");
}
Serial.println("");
}
Here is the python code:
import serial
global ser
ser = serial.Serial("COM8", baudrate=9600, timeout=10,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS)
while(True):
ser.write(bytes(1))
line = ser.readline()
print(line.decode('utf8'))
The output in python comes out to be:
0
LED is OFF
0
LED is OFF
0
LED is OFF
and so on. The ser.write() function isn't writing the value as 1 on the serial port. When I change the value of inputVal in Arduino code, the LED turns on and the output on arduino serial monitor comes as 1 LED is ON, which implies that the circuit connection and Arduino code is working fine.
I also noticed that the COM port that I am using can work with either python or arduino at a time. After uploading the arduino code with inputVal=1, the LED turned on and arduino serial monitor started displaying (1 LED is ON). But, as soon as I ran the python code, the led turned off and the python output came out to be 0 LED is OFF. Please help me.
Also, is there a way for me to control NodeMCU totally by python, without using arduino code first?
the output from python is correct. bytes(integer) creates an array of provided size, all initialized to null in your case size = 1, bytes(1), so the output that you have is 0x00 if you try bytes(10) the out put will be b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'.
what you need to do is to change ser.write(bytes(1)) to ser.write(bytes('1',encoding= 'utf-8')) that should work
I have an issue with sending serial data through python. Let me explain it this way. I have a motor code on arduino which receives data from serial and moves through the received data.
However, whenever I send data in this case 8 lists, it doesn't read the 8th list.
By playing with time.sleep in python code I could see some differences(I could see 4 lists being sent,then I increased time.sleep to 8 and now it sends 7 lists.)
I know playing with time.sleep isn't a solution though. I have also tried emptying input and output buffers and I am out of ideas on what to do next. Here are the codes.
The Python Codes:
The incoming byte will be the data sent as you can see with a for loop, I am sending the lists. The first three values inside each list is to control functions. 1,0,0 means that it will only work the motor function which I have shared in the arduino part. The 3rd and 4th variables here are for controlling the motor, meaning 1st motor will turn 2000 steps and the other will turn for a 1000 steps.
import time
import serial
def close():
arduino=serial.Serial("COM4",9600)
time.sleep(7)
incomingByte=[[1,0,0,2000,1000,1000],[1,0,0,200,100,1000],[1,0,0,250,650,1000],[1,0,0,1000,1000,1000],[1,0,0,2000,1000,1000],[1,0,0,200,100,1000],[1,0,0,250,650,1000],[1,0,0,1000,1000,1000]
length= len(incomingByte)
for i in range(uzunluk):
arduino.write(str.encode(str(incomingByte[i])))
arduino.reset_input_buffer()
arduino.reset_output_buffer()
time.sleep(7)
arduino.close()
close()
The Arduino Code:
void setup() {
pinMode(stepPin1, OUTPUT);
pinMode(dirPin1, OUTPUT);
pinMode(stepPin2, OUTPUT);
pinMode(dirPin2, OUTPUT);
pinMode(enable,OUTPUT);
pinMode(endstop1, INPUT);
pinMode(endstop2, INPUT);
Serial.begin(9600);
void motor(){
for (i=0; i < incomingdata[3]; i++) {
digitalWrite(stepPin1,HIGH);
delay(2);
digitalWrite(stepPin1,LOW);
delay(2);
}
for (i=0; i < incomingdata[4]; i++) {
digitalWrite(stepPin2,HIGH);
delay(2);
digitalWrite(stepPin2,LOW);
delay(2);
}
act_motor=0;
delay(incomingdata[5]);
Serial.flush();
}
void loop() {
if(Serial.available()){
for (a=0; a < 6; a++) {
incomingdata[a] = Serial.parseInt();
}
identifier=(incomingdata[2]);
act_enable=(incomingdata[1]);
act_motor=(incomingdata[0]);
}
if (identifier) homee();
if (act_enable) Enable();
if (act_motor) motor();
}
To simply explain the arduino code; according to the serial data the 3rd and 4th data from each array will be step numbers of how much the stepper motors will move. The 5th data on the list will be the delay for each of the motors kept waiting.
I hope I explained this clearly. I would be glad if anyone could help.
P.S: I am sorry If I have violated any rules. I will be sure to correct my mistakes.
Did you check that the numbers and the order are correct on the Arduino side?
You should always be careful with functions like parseInt that just start somewhere and do some magic.
I recommend to separate your messages using something like \r\n. Then use the normal read() until that sequence is found and until then throw everything away.
Then use parseInt (if you have to) for six times. Then repeat and start looking for the \r\n using read() again.
Currently I'm trying to Send information from python to Arduino through the serial port.
I manage this using only one letter Serial.read() 'P' and executing an action on my Arduino with the following code.
Arduino code:
#define arduinoLED 12 // Arduino LED on board
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(arduinoLED, OUTPUT); // Configure the onboard LED for output
digitalWrite(arduinoLED, LOW); // default to LED off
}
void loop() {
// put your main code here, to run repeatedly:
//delay (20000);
//char comein=Serial.read();
//Serial.println(comein);
char *arg = "hello";
if (Serial.read()== 'P'){
digitalWrite(arduinoLED, HIGH);
delay(5000);
}
else {
digitalWrite(arduinoLED, LOW);
Serial.println("Hello World");
}
}
Python code:
ser.open()
ser.is_open
my_string='P'
my_string_as_bytes=str.encode(my_string)
print(my_string_as_bytes)
ser.write(my_string_as_bytes)
This works well and turn my LED on but how could I manage more then one letter for the command for example 'P1 2018' for the led to turn on?
But my real problem is that I try to do exactly the same thing, using the same Python code, but using SCmd.readSerial() to read the information in Arduino such as the following:
Arduino code:
#include <SerialCommand.h>
#define arduinoLED 12 // Arduino LED on board
SerialCommand SCmd; // The demo SerialCommand object
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(arduinoLED, OUTPUT); // Configure the onboard LED for output
digitalWrite(arduinoLED, LOW); // default to LED off
SCmd.addCommand("P1", process_command1); // Converts two arguments to integers and echos them back
SCmd.addCommand("P", relay1_on); // Turns Relay1 on
SCmd.addDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
Serial.println("Ready");
}
void loop() {
// put your main code here, to run repeatedly:
SCmd.readSerial(); // We don't do much, just process serial commands
}
void relay1_on()
{
digitalWrite(12, HIGH);
Serial.println(3000);
delay(3000);
digitalWrite(12, LOW);
}
void process_command1()
{
int aNumber = 5;
char *arg = "hello";
Serial.println("We're in process_command");
arg = SCmd.next();
int OhmPosition = atoi(arg); //will return only numbers
arg = SCmd.next();
int relay = atoi(arg); //will return only numbers
arg = SCmd.next();
int opentime = atoi(arg); //will return only numbers
Serial.println(OhmPosition);
Serial.println(relay);
Serial.println(opentime);
}
As you can see, their is Serial command, responding to 'P' which is the same example as above but it doesn't work for some reason and don't understand why. Any idea?
And the second Serial command is 'P1' which is where I would like to get at the end, so I could send from Python something like:
Python code:
my_string6 = 'P1'+str(actions_time_[0][0] )+' '+str(actions_time_[0][1])+' '+str(actions_time_[0][2]))
my_string_as_bytes=str.encode(my_string6)
print(my_string_as_bytes)
ser.write(my_string_as_bytes)
output looks like this=> b'P1 150.0 5.0 2000.0 '
To enable me to start the P1 command and send values, to be saved in OhmPosition, Relay, Time which will be separated by a space, as the goal is to pilot a small electrical automate.
I would be very please to have your support on theses couples of point related to each other.
How can your program tell the difference between receiving the "P1" command and receiving the "P" command followed by some random "1"
You code seems to rely on a library that isn't a standard part of the Arduino install. You should provide a link to where you got the SerialCommand class.
I have connected Raspberry pi 2 model B with arduino uno via Bi-Directional Level shifter.
Raspberry pi GND ---------- GND Arduino
3.3v ---------- 5v
SCL ---------- A5
SDA ---------- A4
Hope my I2C connection is correct ?
and my Arduino is connected to 8-Channel Relay Board.
Now I have written code in which I can control the Relay board by Raspberry pi. For ex if i Press '1' the Relay 1 goes high.
Now I want to send data back from arduino to raspberry pi in order to cross check if Relay 1 is high or not, if Relay 1 is high then it should send some data back to Raspberry pi or else not.
My Rpi code is
import smbus
import time
# for RPI version 1, use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)
# This is the address we setup in the Arduino Program
address = 0x04
def writeNumber(value):
bus.write_byte(address, value)
# bus.write_byte_data(address, 0, value)
return -1
def readNumber():
number = bus.read_byte(address)
# number = bus.read_byte_data(address, 1)
return number
while True:
var = input("")
if not var:
continue
writeNumber(var)
number = readNumber()
My Arduino code:
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
#define RELAY1 9
int number = 0;
int state = 0;
void setup() {
pinMode(RELAY1, OUTPUT);
Serial.begin(9600); // start serial for output
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
Serial.println("Ready!");
}
void loop() {
delay(100);
}
// callback for received data
void receiveData(int byteCount){
while(Wire.available()) {
number = Wire.read();
Serial.print("data received: ");
Serial.println(number);
if (number == 1){
if (state == 0){
digitalWrite(RELAY1, HIGH); // set the LED on
state = 1;
}
else{
digitalWrite(RELAY1, LOW); // set the LED off
state = 0;
}
}
}
}
// callback for sending data
void sendData(){
Wire.write(number);
}
Now if I type 1 and due to some loose connection Relay 1 doesn't goes high, So in this case I want the arduino to take data from relay board and send it to Raspberry pi every time.
It will be great if someone can explain also that how it works.
Hope I was able to explain the problem. I have done lots of research but was not able to find some answer.
I am a beginner in python so please help me.
Thanks in advance.
The problem is that you are doing too much inside receiveData, which is called from the Interrupt Service Routine of the I2C utility code, twi.c. You must handle the data quickly, and don't call any other routines that depend on interrupts being enabled (they are disabled during this ISR).
This means you can't call Serial.print, and you can't call any other Wire sending methods. Even calling millis() or micros() is discouraged, as they do take a fair amount of time, and they depend on the TIMER interrupts being handled.
Of course, you are free call Wire.available() and Wire.read(). Actually, byteCount tells you how many bytes are available, so you don't need to call Wire.available() again.
Essentially, your receivedData routine can read the data inside the routine if you're quick about processing it. Otherwise, you can only set a (volatile) flag and then watch for it in loop. From what I see in your sketch, you could do something like this:
// variables that allow signalling between receiveData ISR and loop
volatile bool newData = false;
volatile uint8_t state = false;
// callback for received data
void receiveData(int byteCount)
{
// Read all the bytes; only the last one changes the relay state
while (byteCount-- > 0)
number = Wire.read();
if (state != number) {
state = number;
newData = true;
}
}
// callback for sending data
void sendData(){
Wire.write(number);
}
void loop()
{
if (newData) {
newData = false; // clear the flag for next time
if (number == 1){
digitalWrite(RELAY1, HIGH); // set the LED on
} else {
digitalWrite(RELAY1, LOW); // set the LED off
}
Serial.print("data received: ");
Serial.println( number );
}
}
The delay in loop is unnecessary, and may cause problems if you add something else to loop.
The volatile keyword keeps the compiler from optimizing loop. Without that keyword, the test for newData in loop would disappear because the compiler thinks that newData doesn't change during loop. Why test it? volatile newData tells the compiler that newData can change at any time, like during the receiveData ISR.
And be sure to print the number in the rpi code, as pholtz suggested!
In arduino code
change sendData() function like this
void sendData(){
int relay_status;
relay_status=digitalRead(4);
Wire.write(relay_status);
}
Also in hardware connect one 4th digital pin(or any other free I/O pins) to relay input.
hope it helps:)
Ok, looks like a pretty good start. Two things I want to suggest here.
First, in your python program you should print number so that you can see it's value changing. It's storing your feedback from the Arduino, so you want to display that feedback to the screen. This is as simple as changing number = readNumber() to print readNumber().
Second, in your Arduino program, are you sure that calling Wire.read() returns what you think it does? It looks to me like read() returns a byte. Chances are that when you type 1 it's really being sent as '1', not 1. Char vs. Int. Make sense?
So you might want to check if(number == '1') instead. Just my 2¢.
Your i2c bus is not connected properly. Remove the level shifter, and add 4.7k pull-ups to the 3.3v vcc on scl and sda lines. I2c chips only drive the line low, and require external resistors to pull the line high. This lets you mix voltage levels on your i2c bus fairly easily. Then you can go back to figuring out what your code is doing.
I get random characters when I perform a serial.readline(), sometimes it is other umbers and sometimes it is whole messages. The output should be "1,2" or "2,2"
Here are a couple of screenshots of the output of serial.readline().
I have tried to put a delay at before serial.readline() but it did not make a difference.
There is usually a strange character at the beginning:
I have also received strange messages:
There is a problem later on in the program that causes the program to hand because sometimes I just receive a blank line.
Is there a way to get consistent output from serial?
Here is the arduino code:
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop(){
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn LED on:
digitalWrite(ledPin, HIGH);
Serial.println("1,2");
}
else {
// turn LED off:
digitalWrite(ledPin, LOW);
Serial.println("2,2");
}
}
And here is the python code:
ser = serial.Serial('/dev/ttyUSB0', 9600)
line=ser.readline()
coord= line.strip()
print coord
EDIT:
I tried putting ser.flushInput() after the ser.open() and I get the same output.
Have you flushed the serial buffer
ser.flushInput()
I've been having the same issue when interfacing between pyserial and Arduino. This may be an old post, but in case someone else is having the same trouble, I remedied my problem by inserting:
ser.flushInput()
...right before my ser.readline() call.
How to Read serial data from an Arduino in Linux
Nothing fancy here. I’m getting the current port configuration, setting the input/output speed to 9600 baud, setting the data expectations to be 8 bits per word, with no parity bit and no stop bit, setting canonical mode, and then committing those changes back to the serial port.
If I am not mistaken you have to change the mentioned settings when connecting through serial port.
You do not mention it, but I guess you are using the pySerial library. Regardless you can use it with the correct settings to connect through serial. The constructor of the API allows all the parameters which are noted below:
Pyserial Library
I have not tested this approach as I have had a similar problem in C not Python.
What happens if you break it down to something simpler?
On the Arduino:
void setup()
{
Serial.begin(9600);
}
int i = 0;
void loop()
{
delay(1000);
Serial.print("Hello World ");
Serial.println(i);
i++;
}
And in the Python REPL...
import serial
ser = serial.Serial('/dev/ttyUSB0', 9600)
while(1):
ser.readline()
The first behavior you should observe is that when you initiate the serial connection, the Arduino reboots. This takes a few seconds.
The second behavior you should observe is that (if you're typing this in the REPL slowly) when the while(1) loop begins, you get all of the serial data that had accumulated since you initiated the serial connection. It takes me a few seconds to type all that out, so when I hit Enter after ser.readline() I see:
'Hello World 1\r\n'
'Hello World 2\r\n'
'Hello World 3\r\n'
I only mention this to make sure you're familiar with two things that burned me a bit the last time I messed with serial communication to an Arduino: it needs time to reboot after connecting, and the serial data buffers. readline() will only give you one line in the buffer - not everything that's in the buffer.
Is it possible you're trying to sent/receive data before it's done rebooting? What about button bounce - could a dozen high/low state detections be messing something up?