Using Python and Arduino to change delay of light bulb via Serial - python-3.x

I am using an Arduino Mega and python 3.7 on Windows 10 64-bit. I am trying to make a light bulb blink using python and pyserial. I want the light bulb to stay on for a x amount of time and turn off for an y amount of time. I enter the values in a python Tkinter program: https://pastebin.com/zkRmcP60 full code. after I've entered the values I send into the Arduino via this code:
import msgpack
import serial
arduionoData = serial.Serial('com3', 9600, timeout=1)
def sendlower(*args):
try:
global arduionoData
arduionoData.write(b"1")
while arduionoData.readline().decode() != "Send Next":
pass
first = int(firstdelay.get())
arduionoData.write(msgpack.packb(first, use_bin_type=True))
except ValueError:
print("Only Positive Integers")
def senduppper(*args):
try:
global arduionoData
arduionoData.write(b"2")
while arduionoData.readline().decode() != "Send Next":
pass
second = int(seconddelay.get())
arduionoData.write(msgpack.packb(second, use_bin_type=True))
except ValueError:
print("Only Positive Integers")
The Tkinter program executes the functions above visit Pastebin for entire code.
First I specify the mode or whether or not it's going to be the on delay or the off delay changing.
With this code (Setup and other code omitted please look in the paste bin for it.)
void readdelay(){
mode = Serial.parseInt();
if (mode == 1){
delay(200);
Serial.write("Send Next");
delay1 = Serial.parseInt();
}else if (mode == 2){
delay(200);
Serial.write("Send Next");
delay2 = Serial.parseInt();
}
}
void loop() {
if (Serial.available() > 0){
readdelay();
}
}
Right now if I send in any positive number into the program it either turns off(when i send in a number for the on delay) the light completely or turns it on(when I send in a number for the off delay). My guess is that whenever the Serial.parseInt(); the function gets the wrong type of input it interprets it as a zero.

The documentation says:
If no valid digits were read when the time-out (see Serial.setTimeout()) > occurs, 0 is returned;
It seems the parseInt fails, therefor the delay is set to 0, which is why the light goes completely on or off.
Another possibility it that the arduino only recieves the first character, which means the light switches so fast you cant see it. (Issue described here)
Try printing out what value is received by the arduino. It should tell you what is happening and what direction to go to solve it.

Related

how to trim unknown first characters of string in code vision

I set a mega16 (16bit AVR microcontroller) to receive data from the serial port
which is connected to Bluetooth module HC-05 for attaining an acceptable number
sent by my android app and an android application sends a number in the form of a
string array whose maximum length is equal to 10 digits. The problem arrives
while receiving data such that one or two unknown characters(?) exist at the
beginning of the received string. I have to remove these unknown characters from
the beginning of the string in the case of existence.
this problem is just for HC-05. I mean I had no problem while sending numbers by
another microcontroller instead of android applications.
here is what I send by mobile:
"430102030405060\r"
and what is received in the serial port of microcontroller:
"??430102030405060\r"
or
"?430102030405060\r"
here is USART Receiver interrupt code:
//-------------------------------------------------------------------------
// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if (data==0x0D)
{
puts(ss);printf("\r")
a=0;
memset(ss, '\0', sizeof(ss));
}
else
{
ss[a]=data;
a+=1;
}
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
{
rx_buffer[rx_wr_index++]=data;
if RX_BUFFER_SIZE == 256
// special case for receiver buffer size=256
if (++rx_counter == 0) rx_buffer_overflow=1;
else
if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
if (++rx_counter == RX_BUFFER_SIZE)
{
rx_counter=0;
rx_buffer_overflow=1;
}
endif
}
}
//-------------------------------------------------------------------------
how can I remove extra characters (?) from the beginning of received data in codevision?
You do not need to remove them, just do not pass them to your processing.
You either may test the data character before putting it into your line buffer (ss) or after the complete line was received look for the first relevant character and only pass the string starting from this position to your processing functions.
Var 1:
BOOL isGarbage(char c){
return c<'0' || c > '9';
}
if (data==0x0D)
{
puts(ss);printf("\r")
a=0;
memset(ss, '\0', sizeof(ss));
} else {
if(!isGarbage(data))
{
ss[a]=data;
a+=1;
}
}
Var2:
if (data==0x0D)
{
const char* actualString = ss;
while(isGarbage(*actualString )){
actualString ++;
}
puts(actualString );printf("\r")
a=0;
memset(ss, '\0', sizeof(ss));
} else {
ss[a]=data;
a+=1;
}
However:
maybe you should try to solve the issue in contrast to just fix the symptoms (suppress '?' characters).
What is the exact value of the questionable characters? I suspect, that '?' is only used to represent non printable data.
Maybe your interface configuration is wrong and the sender uses software flow control on the line and the suspicious characters are XON/XOFF bytes
One additional note:
You may run into trouble if you use more complex functions or even peripheral devices from your interrupt service routine (ISR).
I would strongly suggest to only fill buffers there and do all other stuff in the main loop. triggered by some volatile flags data buffers.
Also I do not get why you are using an additional buffer (ss) in the ISR, since it seems that there already is a RX-Buffer. The implementation looks like that there is a good RX-receive buffer implementation that should have some functions/possibilities to get the buffer contents within the main loop, so that you do not need to add your own code to the ISR.
Additional additional notes:
string array whose maximum length is equal to 10 digits.
I count more than that, I hope your ss array is larger than that and you also should consider the fact that something may go wrong on transmission and you get a lot more characters before the next '\n'. Currently you overwrite all your ram.

How can I read and write to the serial buffer in the same Python script?

I'm having a problem understanding how serial works.
I am trying to write a looping script that generates a code in Python, reads the serial buffer for a six digit code entered into the Arduino keypad, sends it back to Python, checks it against a code in an postgreSQL database and returns a value (1 for match, and 0 for no match). I want to send the binary value to the Arduino and trigger a linear servo. When I run the Python loop that I wrote, it functions how I would expect, until I get to the value returned from the SQL database. The value does not seem to write to the serial buffer, and so the Arduino loop never triggers the linear servo. For simplicity of testing, I am generating a code for the keypad locally every time the loop runs.
At first, I was using the keypad.h function waitForKey(), which is blocking. I thought that the blocking was keeping the Arduino loop from registering the updated value and operating the servo. I changed it to getKey(), but the function is still not working. I have tried different encoding methods and writing different types of data (characters and numbers), but nothing seems to work.
Here is the most relevant section of the Python code:
if (txStage == '0'):
#genCode = search_ChatValue("access_code","demo") #Find the value in the database table
time.sleep(10)
while (ser.inWaiting() > 0):
codeDigit = ser.readline(9)
codeDigit = codeDigit.decode()
codeDigit = codeDigit[0:6]
ser.flush()
print(codeDigit)
attemptCode(codeDigit, "demo") #check the entered code against the database code
doorVer = codeResult("demo") #Check if the code was correct
print(doorVer)
if (doorVer == "Success! You may now access your SafeDrop."): #if the code is correct
lockVer = '1' #variable to be sent to the Arduino
print("lock status is: " + str(lockVer))
ser.flush() #flush the serial buffer to ensure it is empty
lockVer = lockVer.encode("utf-8")
ser.write(lockVer) #write to Arduino
time.sleep(10) #wait for lock to open
#time.sleep(5)
Here is the Arduino code:
void loop(){
char pass[6];
char lockVer = '0';
int sensorValue = analogRead(A0);
Door(sensorValue);
door = warning;
if (Serial.available() > 0){
lockVer = Serial.read();
}
if (lockVer == '0'){
while (door == 0){
while (i < 6){
char key = customKeypad.getKey();
if (key){
pass[i] = key;
lcd.write('*');
i++;
if (i == 6){
delay(500);
lcd.clear();
i = 0;
}
}
Serial.println(pass);
Serial.flush();
}
//delay(5000);
//scaleCheck;
}
}
else{
Lock(unlock);
delay(5000);
Lock(lock);
delay(5000);
}
}
My hope is that the variable lockVer would change from 0 to 1, and this change would be identified by the Arduino, causing the linear servo to activate and move into the unlock position, wait 5 seconds, and move back to the locked position. Instead, the Arduino code ignores the change, and keeps looking for keypad input. The keypad code is confirmed to work, and the variable lockVer does change from 0 to 1 in Python, just not in Arduino.
I can post the rest of the code if anyone needs more context, but I am running out of things to try, and I would really appreciate some help. Thanks!
looks like your arduino code is getting stuck in the "while(door == 0)" loop. Change that to an if condition and your code should work as intended

Using RGB Lights with Arduino Struct

I am attempting to use a struct with Arduino to turn on multiple RGB LEDs to specific colors. With this sample code, I created a struct to hold a red pin num, a blue pin num, a blue value, and a red value. I am only using two of the 3 pins on the LED since I only need the colors red, blue, and purple for my application. When I run this code, the incorrect light turns on and in the incorrect color. I am not sure if I correctly understand how to use a struct in the Arduino environment. I used this source http://playground.arduino.cc/Code/Struct to find the basic syntax for a struct in Arduino. I am using it similar to how one might use an object in OOP. I am looking for clarity as to how to use the struct in Arduino and specifically I am not able to get the expected result. I expect for the LED that is connected to pin3 and pin4 to light up purple (as its red and blue pins are both set to HIGH) but instead the LED connected to pin1 and pin2 will light up red (as if pin1 is set to HIGH). Moreover, when I remove the print statements nothing turns on at all (even though this is the only change made). I have checked my wiring countless times and have determined that it is not a hardware issue. Thank you for any help that you can provide.
struct light {
int redPin ;
int bluePin;
int redValue;
int blueValue;
};
light light1;
void setup() {
Serial.begin(9600);
pinMode(light1.redPin, OUTPUT);
pinMode(light1.bluePin, OUTPUT);
light1.redPin = 3;
light1.bluePin = 4;
light1.redValue = HIGH;
light1.blueValue = HIGH;
}
void loop() {
Serial.print(light1.redPin);
Serial.println(light1.redValue);
Serial.print(light1.bluePin);
Serial.println(light1.blueValue);
digitalWrite(light1.redPin, light1.redValue);
digitalWrite(light1.bluePin, light1.blueValue);
}
As Pawel suggested, you are doing things in the wrong order. This would make a lot more sense:
light1.redPin = 3;
light1.bluePin = 4;
light1.redValue = HIGH;
light1.blueValue = HIGH;
pinMode(light1.redPin, OUTPUT);
pinMode(light1.bluePin, OUTPUT);
I am not sure if I correctly understand how to use a struct in the Arduino environment.
It is exactly the same as in C++.
but instead the LED connected to pin1 and pin2 will light up red (as if pin1 is set to HIGH)
Your serial prints will set pin D1 (Tx) to an output and you are seeing your serial prints as turning on the pins.
when I remove the print statements nothing turns on at all
As expected, as you are not sending data to those pins.
The default for an uninitialized global variable is zero, so I would expect that you have set pin D0 (the first pin, labelled Rx) to be an output, and then you are writing to it.
(Edited to add)
Actually, once you have done a Serial.begin the serial hardware takes over pins 0 and 1, and thus attempts to write to them fail.
The output you see on pin D1 is the Serial.print as I mentioned before, and the output on pin D0 is just the internal pull-up used to keep Rx high in the event that you are not using it just now. If you plug in an LED you will see that D0 is duller than D1.

How to read from a Linux serial port

I am working on robot which has to control using wireless serial communication. The robot is running on a microcontroller (by burning a .hex file). I want to control it using my Linux (Ubuntu) PC. I am new to serial port programming. I am able to send the data, but I am not able to read data.
A few piece of code which is running over at the microcontroller:
Function to send data:
void TxData(unsigned char tx_data)
{
SBUF = tx_data; // Transmit data that is passed to this function
while(TI == 0) // Wait while data is being transmitted
;
}
I am sending data through an array of characters data_array[i]:
for (i=4; i<=6; i++)
{
TxData(data_array[i]);
RI = 0; // Clear receive interrupt. Must be cleared by the user.
TI = 0; // Clear transmit interrupt. Must be cleared by the user.
}
Now the piece of code from the C program running on Linux...
while (flag == 0) {
int res = read(fd, buf, 255);
buf[res] = 0; /* Set end of string, so we can printf */
printf(":%s:%d\n", buf, res);
if (buf[0] == '\0')
flag = 1;
}
It prints out value of res = 0.
Actually I want to read data character-by-character to perform calculations and take further decision. Is there another way of doing this?
Note: Is there good study material (code) for serial port programming on Linux?
How can I read from the Linux serial port...
This is a good guide: Serial Programming Guide for POSIX Operating Systems
The read call may return with no data and errno set to EAGAIN. You need to check the return value and loop around to read again if you're expecting data to arrive.
First, take a look at /proc/tty/driver/serial to see that everything is set up correctly (i.e., you see the signals you should see). Then, have a look at the manual page for termios(3), you may be interested in the VMIN and VTIME explanation.

Linux termios VTIME not working?

We've been bashing our heads off of this one all morning. We've got some serial lines setup between an embedded linux device and an Ubuntu box. Our reads are getting screwed up because our code usually returns two (sometimes more, sometimes exactly one) message reads instead of one message read per actual message sent.
Here is the code that opens the serial port. InterCharTime is set to 4.
void COMClass::openPort()
{
struct termios tio;
this->fd = -1;
int tmpFD;
tempFD = open( port, O_RDWR | O_NOCTTY);
if (tempFD < 0)
{
cerr<< "the port is not opened"<< port <<"\n";
portOpen = 0;
return;
}
tio.c_cflag = BaudRate | CS8 | CLOCAL | CREAD ;
tio.c_oflag = 0;
tio.c_iflag = IGNPAR;
newtio.c_cc[VTIME] = InterCharTime;
newtio.c_cc[VMIN] = readBufferSize;
newtio.c_lflag = 0;
tcflush(tempFD, TCIFLUSH);
tcsetattr(tempFD,TCSANOW,&tio);
this->fd = tempFD;
portOpen = true;
}
The other end is configured similarly for communication, and has one small section of particular iterest:
while (1)
{
sprintf(out, "\r\nHello world %lu", ++ulCount);
puts(out);
WritePort((BYTE *)out, strlen(out)+1);
sleep(2);
} //while
Now, when I run a read thread on the receiving machine, "hello world" is usually broken up over a couple messages. Here is some sample output:
1: Hello
2: world 1
3: Hello
4: world 2
5: Hello
6: world 3
where number followed by a colon is one message recieved. Can you see any error we are making?
Thank you.
Edit:
For clarity, please view section 3.2 of the Linux Serial Programming HOWTO. To my understanding, with a VTIME of a couple seconds (meaning vtime is set anywhere between 10 and 50, trial-and-error), and a VMIN of 1, there should be no reason that the message is broken up over two separate messages.
I don't see why you are surprised.
You are asking for at least one byte. If your read() is asking for more, which seems probable since you are surprised you aren't getting the whole string in a single read, it can get whatever data is available up to the read() size. But all the data isn't available in a single read so your string is chopped up between reads.
In this scenario the timer doesn't really matter. The timer won't be set until at least one byte is available. But you have set the minimum at 1. So it just returns whatever number of bytes ( >= 1) are available up to read() size bytes.
If you are still experiencing this problem (realizing the question is old), and your code is accurate, you are setting your VTIME and VMIN in the newtio struct, and the rest of the other parameters in the tio struct.

Resources