AndersM
Posts: 33
Joined: Sun Sep 06, 2015 1:18 pm

reading serial data from sensor

Tue Sep 22, 2015 6:19 pm

Hi!

I have a ATTiny and uses it to read analog input from a sensor and send it through serial communication to the Pi. Now I want to make a program (in Python) that reads the a analog input approx. 30 times per second. What is the best approach to get latest data to the Pi as soon as possible?

I thinking of doing this:
1 Pi sends a character to the ATTiny thats waits for this signal
2) the ATTiny reads and sends the sensor data to the Pi and goes back into waiting
3) Pi receives the data and does its stuff then back to 1

Comments?

User avatar
joan
Posts: 14697
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: reading serial data from sensor

Tue Sep 22, 2015 7:07 pm

Is there any reason you can't continuously write serial data from the ATtiny to the Pi?

You can flush the receive buffer at the Pi end when it starts up to discard historical data and at the ATtiny end you could ignore buffer full errors on writes.

AndersM
Posts: 33
Joined: Sun Sep 06, 2015 1:18 pm

Re: reading serial data from sensor

Wed Sep 23, 2015 10:41 am

I'm new to programming serial communication so I am guessing from what I managed to understand (misunderstand?) .

I thought that continuously write serial data might make the Pi read old data if the ATTiny sends it faster than the Pi reads it.
But flushing the receive buffer solves that I assume. Could flushing the input buffer corrupt the data if it's done while the ATTiny is in the middle of sending the data string so I have to make a check for that?

Any example/tutorial on this?

AndersM
Posts: 33
Joined: Sun Sep 06, 2015 1:18 pm

Re: reading serial data from sensor

Sun Sep 27, 2015 12:32 pm

Did some testing with pygame and serial input.
1) Not flushing input gives a very (many seconds) delayed response
2) Flushing gives input errors when part of the transmission is flushed. (Like: 1023 becomes 23)

Ok. It worked as I thought
Now. How to avoid the problem?

Edit:
Fixed the problem by letting serial.readline be the clock in the game. removed the clock.tick(30) in the game loop. But there ought to be a better solution to get serial input read and independently pacing the program.
Last edited by AndersM on Sun Sep 27, 2015 12:51 pm, edited 1 time in total.

ame
Posts: 3172
Joined: Sat Aug 18, 2012 1:21 am
Location: Korea

Re: reading serial data from sensor

Sun Sep 27, 2015 12:50 pm

You need some kind of marker in your data stream. Discard anything that does not start with the marker.

For example, NMEA data from a GPS starts with $ and ends with a carriage return. You can do something similar.

AndersM
Posts: 33
Joined: Sun Sep 06, 2015 1:18 pm

Re: reading serial data from sensor

Sun Sep 27, 2015 5:01 pm

The marker idea seems good for avoiding partial data loss due to input flush.

But now I'm thinking of moving the serial input read to a separate thread so that this thread will be reading at least as fast as the tiny is writing. Then have the reading thread updating the input variable in the main program.

User avatar
experix
Posts: 204
Joined: Mon Nov 10, 2014 7:39 pm
Location: Coquille OR
Contact: Website

Re: reading serial data from sensor

Mon Sep 28, 2015 4:19 pm

I needed to operate a Gertbot (which uses serial communication for everything) at high speed. Sending or receiving in bulk is not an option because you have to send a little command and then get a little answer, and so on. But the operating system interface is a big (or should I say little?) bottleneck. I got about a 7x speedup by writing serial port operating code in user-space, using a non-interrupt technique. You can find the details in my latest code updates at [url]http:experix.sourceforge.net[/url] in the 'dist' tarball, in source/xpx_local, in the files named 'gpioRPi...'.

bjs69296
Posts: 2
Joined: Tue Sep 29, 2015 9:28 am

Re: reading serial data from sensor

Tue Sep 29, 2015 10:02 am

How can we read serial data from 8051 in the Pi?

bjs69296
Posts: 2
Joined: Tue Sep 29, 2015 9:28 am

Re: reading serial data from sensor

Tue Sep 29, 2015 10:10 am

How can I read serial data from a Atmel89LP6440 using the Raspberry pi ? please help me

AndersM
Posts: 33
Joined: Sun Sep 06, 2015 1:18 pm

Re: reading serial data from sensor

Tue Sep 29, 2015 11:21 am

Interesting! ... but a bit over my head :oops:

danjperron
Posts: 3440
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: reading serial data from sensor

Wed Sep 30, 2015 11:42 am

I practically always use a thread for serial communication. This way is not on the main loop and it could be asynchronous. The advantage of this is that you could have more than one serial and it will be handle the same way.

this an example using thread to read GPS data.
viewtopic.php?p=661794#p661794

How can we read serial data from 8051 in the Pi?
How can I read serial data from a Atmel89LP6440 using the Raspberry pi ? please help me
Well like any other serial communication you select the correct baud, number of bits , parity and the proper handshake and you read the serial port byte per byte and figure out the rest.

Nobody can't answer your question because you don't give enough info!
What is the protocol. tty serial, binary, PPP, modbus , etc....
What is the hardware of the serial port . ttl 5V /3V, RS-232C, RS-422, RS-485 ?

AndersM
Posts: 33
Joined: Sun Sep 06, 2015 1:18 pm

Re: reading serial data from sensor

Tue Oct 06, 2015 11:10 am

danjperron wrote:I practically always use a thread for serial communication. This way is not on the main loop and it could be asynchronous.
I made a small "pong" game to test my analog input using a separate thread for reading the input. It works fine so I will continue down that path.

Code: Select all

#!/usr/bin/env python

import serial, threading, time
import pygame, sys, os
from pygame.locals import *

# INTIALISATION
BAT_WIDTH=20
BAT_HEIGHT=50
BAT_RIM = 20
BALL_WIDTH=20
BASE_SPEED=4
speed=BASE_SPEED
input1 = 300-BAT_WIDTH/2
input2 = 300-BAT_WIDTH/2
ball_v_x = -speed
ball_v_y = 0
bat1_pos_x =100-BAT_WIDTH
bat1_pos_y =300-BAT_HEIGHT/2
bat2_pos_x =1100
bat2_pos_y =300-BAT_HEIGHT/2
ball_pos_x = bat2_pos_x-BALL_WIDTH-5
ball_pos_y = 300-BALL_WIDTH/2
player1_scr = 0
player2_scr = 0

#Bounce function for the bats
def bounce(bat_y) :
    global ball_v_x, ball_v_y
    if ball_pos_y + BALL_WIDTH >= bat_y and ball_pos_y + BALL_WIDTH < bat_y + BAT_RIM :
        ball_v_x = -ball_v_x
        ball_v_y = max(-speed, ball_v_y-speed)
    elif ball_pos_y > bat_y + BAT_HEIGHT - BAT_RIM and ball_pos_y  <= bat_y + BAT_HEIGHT :
        ball_v_x = -ball_v_x
        ball_v_y = min(speed, ball_v_y+speed)
    elif ball_pos_y >= bat_y and ball_pos_y <= bat_y + BAT_HEIGHT :
        ball_v_x = -ball_v_x

#Serial reader run in a separate daemon thread        
def serial_reader() :
    global input1, input2
    ser = serial.Serial(
        port='/dev/ttyUSB0',
        baudrate = 9600,
        parity=serial.PARITY_NONE,
        stopbits=serial.STOPBITS_ONE,
        bytesize=serial.EIGHTBITS,
        timeout=1
        )

    while 1:
        x=(ser.readline()).rstrip()
        numbers=x.split(',')
        oi1=input1
        oi2=input2
        try:
            input1=int(int(numbers[0])*(600-BAT_HEIGHT)/1023)
            input2=int(int(numbers[1])*(600-BAT_HEIGHT)/1023)
        except :
            input1=oi1
            input2=oi2
            print sys.exc_info()[0]
        time.sleep(0)   

#Start of main program
#Start serial reader thread           
if __name__ == '__main__':
    sr = threading.Thread(name='serial-reader', target=serial_reader)
    sr.setDaemon(True)
    sr.start()

#Init pygame stuff
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((1200, 600))
pygame.display.set_caption("Pong", 'Pong')
pygame.font.init()
font = pygame.font.SysFont('monospace', 48)

#Main loop
while 1:
    clock.tick(120) #Update screen every 1/120 s
    
    # User input events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit(); sys.exit();
        if not hasattr(event, 'key'): continue
        if event.key == K_ESCAPE: sys.exit(0) # quit the game
        elif event.key == K_UP: speed=min(BAT_WIDTH-2,speed+1)
        elif event.key == K_DOWN: speed=max(1,speed-1)
        elif event.key == K_r:
            ball_pos_x = bat2_pos_x-BALL_WIDTH-5
            ball_pos_y = 300-BALL_WIDTH/2
            speed=BASE_SPEED
            ball_v_x = -speed
            ball_v_y = 0
            player1_scr = 0
            player2_scr = 0
     
    #Updating bat position from serial input   
    bat1_pos_y=input1  
    bat2_pos_y=input2
    
    #Checking bounce or miss    
    if ball_pos_y < 0 or ball_pos_y+BALL_WIDTH > 600: 
        ball_v_y = -ball_v_y
    if ball_pos_x+BALL_WIDTH > 1200:
        ball_v_x = speed
        ball_v_y = 0
        ball_pos_y = 300-BAT_HEIGHT/2
        ball_pos_x= bat1_pos_x + BAT_WIDTH+5
        player1_scr +=1
    if ball_pos_x < 0:
        ball_v_x = -speed
        ball_v_y = 0
        ball_pos_y = 300-BAT_HEIGHT/2
        ball_pos_x= bat2_pos_x-BALL_WIDTH-5
        player2_scr +=1       
    if ball_pos_x < bat1_pos_x + BAT_WIDTH and ball_pos_x > bat1_pos_x :
        bounce(bat1_pos_y)  
    if ball_pos_x + BALL_WIDTH > bat2_pos_x and ball_pos_x < bat2_pos_x + BAT_WIDTH :
        bounce(bat2_pos_y)
    
    #Updating position of ball
    ball_pos_x +=ball_v_x
    ball_pos_y +=ball_v_y

    # Rendering
    screen.fill((0,0,0))
    pygame.draw.rect(screen, (255,255,255), (bat1_pos_x,bat1_pos_y,BAT_WIDTH,BAT_HEIGHT), 0)
    pygame.draw.rect(screen, (255,255,255), (bat2_pos_x,bat2_pos_y,BAT_WIDTH,BAT_HEIGHT), 0)
    pygame.draw.rect(screen, (255,255,255), (ball_pos_x,ball_pos_y,BALL_WIDTH,BALL_WIDTH), 0)
    text = font.render(str(player1_scr) + " - " + str(player2_scr), 0, (255,255,255))
    textrect = text.get_rect()
    textrect.centerx = screen.get_rect().centerx
    textrect.centery = 25
    screen.blit(text, textrect)
    pygame.display.flip()
    #print pygame.time.get_ticks()
The serial input receives new position data approx 60 times/second from an ATTiny85 with two potentiometers attached to analogue input pins.

Return to “Automation, sensing and robotics”