flightvision
Posts: 21
Joined: Sat Apr 21, 2012 9:04 am

Linux/C: Set baudrate before opening serial/UART; Servo

Sat Jul 28, 2012 11:51 pm

I am on a project with the wonderful Pi for quite a while now. I am going to write up on it more detailed soon. In 1 sentence it would be described with "Control RC-airplane over an IP-network aka WLAN".

The part I am asking for help with is about serial communication from C/Linux.

<sidenote>
After having gone the "try to control servo with GPIO -> too fuzzy with Debian/Raspbian / try Realtime Kernel and optimise C program -> still too fuzzy (20us is still too much)"-path and then finally admitting that one can not get really quality servo control by bitbanging for critical applications from Linux... I have bought a Pololu 207 servo controller board to do the time-critical stuff. I learned a lot while doing this, but for model RC planes I just wasn't satisfied with the precision.
</sidenote>

So I am trying to talk to this module now via Pi's UART:
Pololu 207: http://www.pololu.com/catalog/product/207/resources
User guide: http://www.pololu.com/file/0J37/ssc03a_guide.pdf

Here is my wiring:
Image
I have added the resistor for additional security although not really needed I think. I do only need Pi's Tx, Rx is not connected.

Power on, everything seems fine: yellow LED on the Pololu. Whenever I open the serial interface /dev/ttyAMA0, I get the LED signal 'red + fast green' from the Pololu 207, which shall mean 'Baudrate too high' according to the manual.
I open it in C via (pseudocode) (I peeked at the great wiringPi-library to see if I am completely of):

Code: Select all

fd = open("/dev/ttyAMA0", ...)
tcgetattr(fd, &options)
cfsetospeed(...)
tcsetattr(...)
[tcflush(...)]
[ioctl(...)]
close(fd)
...or with the minicom program: 'minicom -b 19200 -D /dev/ttyAMA0'

The Pololu gives the error signal immediately after the 'open(...)'.

So I guess I shall set the baudrate before opening the device. This is where I am stuck. I need a file handle to get options (tcgetattr()) and set the baudrate with cfsetospeed().

I am not sure if my basic understanding of the UART is right, but I think it will start sending zeros and stopbits as soon as I open the device, but unfortunately it is by default at 115k Baud and the Polulu expects 10k-40k.
Could it also be that the signal interpretes 0 as 'high' which would be wrong for the Pololu?

The voltage of 3v3 is OK for the 'logic-level serial input', according to the manual.

Any help is appreciated.

CapnBry
Posts: 28
Joined: Sun Jan 08, 2012 2:04 pm
Contact: Website

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Sun Jul 29, 2012 12:16 am

The Pololu serial device haven't any experience with, but I do have a lot of experience with serial communication under Linux so maybe I can provide some insight into that.

When you open() a serial device, no data is transmitted. Serial is known as "asynchronous communication" in that the first bit of any transmitted data, the start bit, sets the clock for the bits of the byte that follow. It doesn't start transmitting as soon as the device is opened.

What might be happening is that when you open the device there's a kernel log message being sent out the UART perhaps? Check your `cat /proc/cmdline` to make sure there's no syslog output going to it, and also check your /etc/inittab to make sure there's no getty running on it which might be competing with you.

flightvision
Posts: 21
Joined: Sat Apr 21, 2012 9:04 am

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Sun Jul 29, 2012 12:39 am

Thanks Cap'n!

cat /proc/cmdline

Code: Select all

dma.dmachans=0x3c bcm2708_fb.fbwidth=1584 bcm2708_fb.fbheight=954 bcm2708.boardrev=0x2 bcm2708.serial=0xc2e004fb smsc95xx.macaddr=B8:27:EB:E0:04:FB dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p3 rootfstype=ext4 elevator=noop rootwait
There are only getty's on tty1,2,3 in /etc/inittab

`lsof | grep ttyAMA` does not yield any process. `lsof | grep tty` yields the getty's and console-k on tty0.

One other test I did: when I short-wire Pi's Rx to Tx (over a 10k resistor) and start `minicom -b 19200 -D /dev/ttyAMA0` I see the echo of my keypresses and no readable characters apart from that. What could I check to see if any non-readable characters are transmitted (I mean during opening the device)?

CapnBry
Posts: 28
Joined: Sun Jan 08, 2012 2:04 pm
Contact: Website

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Sun Jul 29, 2012 2:35 am

Oh nice, you've obviously done your homework on this! It looks like you've got a good command line that doesn't mess up the baud detection on the servo controller.

This is just a shot in the dark, and I hope it isn't insulting, but you're not trying to set the baud rate directly in the cfsetospeed() call are you? They take symbolic constants and not integer. For example here's some code I use:

Code: Select all

#include <termios.h>

static struct baud_mapping baud_lookup_table [] = {
  { 1200,   B1200 },
  { 2400,   B2400 },
  { 4800,   B4800 },
  { 9600,   B9600 },
  { 19200,  B19200 },
  { 38400,  B38400 },
  { 57600,  B57600 },
  { 115200, B115200 },
  { 230400, B230400 },
  { 0,      0 }                 /* Terminator. */
};

static speed_t serial_baud_lookup(long baud)
{
  struct baud_mapping *map = baud_lookup_table;

  while (map->baud) {
    if (map->baud == baud)
      return map->speed;
    map++;
  }

  fprintf(stderr, "%s: serial_baud_lookup(): unknown baud rate: %ld\n",
          progname, baud);
  exit(1);
}

int ser_setspeed(int fd, long baud, int flushfirst)
{
  int rc;
  struct termios termios;
  speed_t speed = serial_baud_lookup (baud);

  if (!isatty(fd))
    return -ENOTTY;


  /*
   * initialize terminal modes
   */
  rc = tcgetattr(fd, &termios);
  if (rc < 0) {
    fprintf(stderr, "%s: ser_setspeed(): tcgetattr() failed",
            progname);
    return -errno;
  }

  /*
   * copy termios for ser_close if we haven't already
   */
  if (! saved_original_termios++) {
    original_termios = termios;
  }

  termios.c_iflag = IGNBRK;
  termios.c_oflag = 0;
  termios.c_lflag = 0;
  termios.c_cflag = (CS8 | CREAD | CLOCAL);
  termios.c_cc[VMIN]  = 1;
  termios.c_cc[VTIME] = 0;

  cfsetospeed(&termios, speed);
  cfsetispeed(&termios, speed);

  rc = tcsetattr(fd, (flushfirst) ? TCSAFLUSH : TCSANOW, &termios);
  if (rc < 0) {
    fprintf(stderr, "%s: ser_setspeed(): tcsetattr() failed\n",
            progname);
    return -errno;
  }
  return 0;
}


flightvision
Posts: 21
Joined: Sat Apr 21, 2012 9:04 am

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Sun Jul 29, 2012 4:58 am

Thank you for being so polite but don't fear you could insult me :) Massive thanks for helping me!
I took the defined B19200 constant but I guess you pointed me in the correct direction ;)

I opened the device via C. Then I watched the output of `stty -F /dev/ttyAMA0` which said the UART would run at a baudrate of 134 Baud :ugeek: Cross-checked with an open minicom, and it returns the correct baudrate then...

From there I realized I OR'ed some termios.c_oflag constants into my termios.c_cflag variable which messed up the baudrate that cfsetospeed() sets there, too. Problem 1 fixed, thanks to you CapnBry :)

Problem 0: Pololu always said (and still does) 'Baudrate too high' after opening the device. If I reset the Pololu after opening and then send control sequences to it, it accepts them. Very handy that the Pololu has a 'RESET' pin... Wired it over a resistor to a GPIO on the Pi and pull it low after opening the device. Not an explanation at all, but a workaround...

Those servos are moving and moving and moving now, superprecise with 5000 steps :D Now Cap'n, how can I help you please?

CapnBry
Posts: 28
Joined: Sun Jan 08, 2012 2:04 pm
Contact: Website

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Sun Jul 29, 2012 5:59 pm

That's awesome! I'm glad you found the problem. I try to be as polite as possible to people who know what they're doing programming-wise because it's easy to come off sounding insulting when you supply a somewhat obvious solution. When you're done with your project, whatever it is, make sure you post more about it so we can all steal your ideas!

User avatar
gordon@drogon.net
Posts: 1970
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Tue Jul 31, 2012 8:40 am

flightvision wrote:I am on a project with the wonderful Pi for quite a while now. I am going to write up on it more detailed soon. In 1 sentence it would be described with "Control RC-airplane over an IP-network aka WLAN".

The part I am asking for help with is about serial communication from C/Linux.
Just catching up and it looks like you're going now, but I recently put some simplified serial port handling into my wiringPi library - nothing more than wrappers round the existing system calls, but might make it a little easier than remember all the various ioctls, etc.

https://projects.drogon.net/raspberry-p ... l-library/

-Gordon
--
Gordons projects: https://projects.drogon.net/

flightvision
Posts: 21
Joined: Sat Apr 21, 2012 9:04 am

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Tue Jul 31, 2012 12:38 pm

I started from your wiringPi serial.h and stopped at the point where the device would signal "Baudrate too high" on the open(). From there I started my own. With the work-around I found now, I could switch back to wiringPi (which I already use for GPIO).

Thank you for that library!

Raf41
Posts: 1
Joined: Sat Feb 02, 2013 12:43 pm

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Sat Feb 02, 2013 1:22 pm

Hello Guys!

Sorry for warming up such old topic.
I am doing something similar to you. My idea however is to use 3g network for remote connection. A will use Pi and Arduino. Right now i am trying to write code for serial communications in C, and from this tread i`ve found out that i can use wiringPI to achieve this.

All ready i can control one servo using minicom. And here is my question, can you post some simple code for serial communications i C based on wiringPi?

And little off-topic... hows project going?

User avatar
gordon@drogon.net
Posts: 1970
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Sat Feb 02, 2013 2:03 pm

Raf41 wrote:Hello Guys!

Sorry for warming up such old topic.
I am doing something similar to you. My idea however is to use 3g network for remote connection. A will use Pi and Arduino. Right now i am trying to write code for serial communications in C, and from this tread i`ve found out that i can use wiringPI to achieve this.

All ready i can control one servo using minicom. And here is my question, can you post some simple code for serial communications i C based on wiringPi?

And little off-topic... hows project going?
There are some demos in the examples directory that comes with wiringPi:

cd
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build
cd examples

then you'll find: serialRead.c and serialTest.c

serialRead.c just reads and displays characters coming in, serialTest.c sends data out and waits for it to come back in.

-Gordon
--
Gordons projects: https://projects.drogon.net/

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

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Thu Mar 14, 2013 2:39 am

For me the pololu servo controller is a must. GPIO pins are not too many.

this is a example in python for people how want to use the pololu serial controller

This is the code I use to manually control servo on my small robot car driven by a raspberry pi. I use the serial Pololu servo controller . I want to expand my robot later to build some kind of automatic gardener, water the plant , cut the grass chase the neighbour cat ,etc..

GPIO usage is an issue and the controller only use 2 I/O and one could be daisy chain, the serial one, when I will build my bigger robot using a Sabertooth motor controller.

The pololu need 5 Volts so I use one spark fun level converter between the RPI and the servo controller.

GPIO 4 is use to Reset the pololu since the RPI send a invalid character when you open the port.
GPIO 14 is the serial transmit . ( we don't need the serial RXD on the servo controller)

GPIO4 => TXi 3.3V to 5V converter TXo => pololu reset
GPIO14 => TXi 3.3V to 5V converter TXo => pololo ttl serial in


this is my script

Code: Select all

#!/usr/bin/python
from time import sleep
import serial
import sys
import RPi.GPIO as GPIO
out = serial.Serial('/dev/ttyAMA0',38400)

#use GPIO4 has servo reset
ServoReset = 7


if __name__ == '__main__':
  if len(sys.argv) is 3:
    Servo    = int(sys.argv[1])
    Position = int(sys.argv[2])
  else:
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(ServoReset,GPIO.OUT)
    GPIO.output(ServoReset,False)
    GPIO.output(ServoReset,True)
    sleep(0.1)
    #write to an unused servo just to sync
    out.write('\x80\x01\x04\x7f\0x11\0x11')
    print('Servo initialized!')
    sys.exit()

out.write('\x80\x01\x04' + chr(Servo) + chr(Position >>7) + chr(Position & 0x7f))
the arguments are 'Servo number' and 'Servo Position' (500 to 5000, centre at 3000).

Omitting the argument will force pololu reset

I use pololu command 4 which is a range between 500 and 5000 with the centre at 3000. But it is easy to modify the script to use command 3. Pololu protocol use 8 bits data but only the first character has MSB on. A servo position value of n is split in 2 bytes , first = n/128 and second is n % 128.(struct.pack won't work).

python servo.py 0 3000 will center servo 0
python servo.py 1 500 will rotate servo1 roughly 90 degree from the centre position.

P.S. don't forget to remove the serial console from the /boot/cmdline.txt
remove the string "console=ttyAMA0,115200 kgdboc=ttyAMA0,115200"

Because the ttyAMA0 port send something at startup, the pololu servo hangs right away. (Baud rate recognition). We need to reset it to set the correct baud rate. Just need to be reset once.

The simplest thing is to add in the /etc/rc.local file the command to reset the pololu
/usr/bin/python /usr/local/bin/servo.py

In this example , my python script is in /usr/local/bin

this is the picture of my small Rpi car . Webcam using mjpeg_streamer for video and ffmpeg for the mike. L298N IC with 12V AA battery to drive the wheel motor. A cheap $5 audio speaker for the sound, a long range wifi and a ISound battery pack to deliver the 5 volts power. Next step will be to add the frontal jaw , 2 servos ,to grab some stuff.

Image

Daniel

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

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Mon Mar 25, 2013 12:37 am

This is a video of the previous post robot car

http://dl.dropbox.com/u/48891705/RobotCar.mpg

I use 3 servos connected to the pololu serial controller. In the rc.local I reset the servo controller and send command to an unknow servo to set the serial baud rate.

After that I just use a python script to pass the command using keyboard keys.

this is the full python script about this current settings. Inside this script you have the function to read keyboard keys and also play sound.

Code: Select all

#!/usr/bin/env python
#this program will control the robot car using keyboard keys
#Ce programme permet de contrôler  la voiture robot avec des touches de clavier
#  keys function
#
#   car move   ( mouvement du véhicule)
#   w : forwad  (avancer)
#   a : left    (tourner à gauche)
#   s : right   (tourner à droite)
#   z : reverse (reculer)
#   
#   Webcam
#   i :  up   (caméra vers le haut)
#   j :  left (caméra vers la gauche)
#   k :  right (caméra vers la droite)
#   m :  down  (caméra vers le bas)
#
#
#  Jaw   (la pince)
#   o : open (ouvrir)
#   p : close (fermer)
#
#
#   v : turn all servos off (Fermer tout les servos)
#
#
#  Sound   ( le son)
# keys 0 to 9  play different sound 
# les clef de 0 à 9 jouent différents son. (0 = sirène ambulance , 9= R2d2, etc…)
#
# any other key will stop the movement of the car
# toute clef non déclaré arête le véhicule

from time import sleep
import RPi.GPIO as GPIO
import sys, termios, atexit
from select import select   
import serial
import ossaudiodev
import wave
from threading import Thread

out = serial.Serial('/dev/ttyAMA0',38400)


CamX = int(3000)
CamY = int(3000)

#set io pin

EnA = 11
EnB = 15
Out1 = 24
Out2 = 21
Out3 = 19
Out4 = 23

GPIO.setmode(GPIO.BOARD)


# save the terminal settings
fd = sys.stdin.fileno()
new_term = termios.tcgetattr(fd)
old_term = termios.tcgetattr(fd)

# new terminal setting unbuffered
new_term[3] = (new_term[3] & ~termios.ICANON & ~termios.ECHO)

# switch to normal terminal
def set_normal_term():
    termios.tcsetattr(fd, termios.TCSAFLUSH, old_term)

# switch to unbuffered terminal
def set_curses_term():
    termios.tcsetattr(fd, termios.TCSAFLUSH, new_term)

def putch(ch):
    sys.stdout.write(ch)

def getch():
    return sys.stdin.read(1)

def getche():
    ch = getch()
    putch(ch)
    return ch

def kbhit():
    dr,dw,de = select([sys.stdin], [], [], 0)
    return dr <> []


def Forward():
   GPIO.output(Out1,True)
   GPIO.output(Out3,True)
   GPIO.output(Out2,False)
   GPIO.output(Out4,False)
   GPIO.output(EnA,True)
   GPIO.output(EnB,True)

def Stop():
   GPIO.output(Out1,False)
   GPIO.output(Out3,False)
   GPIO.output(Out2,False)
   GPIO.output(Out4,False)
   GPIO.output(EnA,True)
   GPIO.output(EnB,True)

def Standby():
   GPIO.output(Out1,False)
   GPIO.output(Out3,False)
   GPIO.output(Out2,False)
   GPIO.output(Out4,False)
   GPIO.output(EnA,False)
   GPIO.output(EnB,False)

def Reverse():
   GPIO.output(Out2,True)
   GPIO.output(Out4,True)
   GPIO.output(Out1,False)
   GPIO.output(Out3,False)
   GPIO.output(EnA,True)
   GPIO.output(EnB,True)

def TurnRight():
   GPIO.output(Out1,True)
   GPIO.output(Out3,False)
   GPIO.output(Out2,False)
   GPIO.output(Out4,True)
   GPIO.output(EnA,True)
   GPIO.output(EnB,True)

def TurnLeft():
   GPIO.output(Out1,False)
   GPIO.output(Out3,True)
   GPIO.output(Out2,True)
   GPIO.output(Out4,False)
   GPIO.output(EnA,True)
   GPIO.output(EnB,True)


def ServoSpeed(id , speed):
 out.write('\x80\x01\x01' + chr(id) + chr(speed))
 print("Servo ",id,"speed = ",id,speed)

def ServoSleep(id):
 out.write('\x80\x01\x00' + chr(id) + chr(15))
 print("Servo ",id," to sleep")

def Servo( id , ServoPosition):
 out.write('\x80\x01\x04' + chr(id) + chr(ServoPosition >>7) + chr(ServoPosition & 0x7f))
 print("Servo ",id," = ",ServoPosition)
def CloseJaw():
 Servo(2,4400)

def OpenJaw():
 Servo(2,1000)

def play(filename):
  print "opening file"
  sound_file = wave.open(filename,'rb')

  print "getting parameters"
  (nc, sw, fr, nf, comptype, compname) = sound_file.getparams()

  print "parameters were",  (nc, sw, fr, nf, comptype, compname)
  print "opening audio"
  sound = ossaudiodev.open('w')

  print "setting parameters"
  sound.setparameters(ossaudiodev.AFMT_S16_NE, nc, fr)

  print "readframes"
  data = sound_file.readframes(nf)

  print "closing file"
  sound_file.close()

  print "writing data"
  sound.write(data)

  # Never gets here, freezes on the previous line.
  print "closing sound device"
  sound.close()


def play0():
   play("s0.wav")

def play1():
   play("s1.wav")

def play2():
   play("s2.wav")

def play3():
   play("s3.wav")

def play4():
   play("s4.wav")

def play5():
   play("s5.wav")

def play6():
   play("s6.wav")

def play7():
   play("s7.wav")

def play8():
   play("s8.wav")

def play9():
   play("s9.wav")

#============ MAIN ==============
homeX = 2800
homeY = 3000
CamX = homeX
CamY = homeY

Servo(0,CamX+70)
Servo(1,CamY)
ServoSpeed(0,32)
ServoSpeed(1,32)

GPIO.setmode(GPIO.BOARD)

# set enable a as output
GPIO.setup(EnA,GPIO.OUT)
GPIO.output(EnA, False)

#set enable b as output

GPIO.setup(EnB,GPIO.OUT)
GPIO.output(EnB, False)


#set enable out 1,2,3 and 4  as output

GPIO.setup(Out1,GPIO.OUT)
GPIO.setup(Out2,GPIO.OUT)
GPIO.setup(Out3,GPIO.OUT)
GPIO.setup(Out4,GPIO.OUT)


GPIO.output(Out1,False)
GPIO.output(Out2,False)
GPIO.output(Out3,False)
GPIO.output(Out4,False)



#Forward()
#sleep(1)
#Reverse()
#sleep(1)
#TurnLeft()
#sleep(5)
#TurnRight()
#sleep(5)
#Stop()
#sleep(1)
#Standby()

if __name__ == '__main__':
    atexit.register(set_normal_term)
    set_curses_term()

## end of http://code.activestate.com/recipes/572182/ }}}
while True:
  key = getch()
  if key== "q":
   print "exit"
   break
  elif key=="w":
   Forward()
  elif key=="a":
   TurnLeft()
  elif key=="s":
   TurnRight()
  elif key=="z":
   Reverse()
  elif key=="o":
   OpenJaw()
  elif key=="p":
   CloseJaw()
  elif key=="m":
   CamY=CamY - 100
   if CamY < 2200:
    CamY = 2200
   Servo(1,CamY)
  elif key=="i":
   CamY=CamY +100
   if CamY >3600:
    CamY = 3600
   Servo(1,CamY)
  elif key=="j":
   CamX=CamX - 200
   if CamX < 800:
    CamX = 800
   Servo(0,CamX+70)
  elif key=="k":
   CamX= CamX + 200
   if CamX > 4800:
     CamX = 4800
   Servo(0, CamX+70)
  elif key=="h":
    CamX=homeX
    Servo(0,CamX+70)
    CamY=homeY
    Servo(1,CamY)
  elif key=="0":
     thread0 = Thread(target = play0)
     thread0.start()
  elif key=="1":
     thread1 = Thread(target = play1)
     thread1.start()
  elif key=="2":
     thread2 = Thread(target = play2)
     thread2.start()
  elif key=="3":
     thread3 = Thread(target = play3)
     thread3.start()
  elif key=="4":
     thread4 = Thread(target = play4)
     thread4.start()
  elif key=="5":
     thread5 = Thread(target = play5)
     thread5.start()
  elif key=="6":
     thread6 = Thread(target = play6)
     thread6.start()
  elif key=="7":
     thread7 = Thread(target = play7)
     thread7.start()
  elif key=="8":
     thread8 = Thread(target = play8)
     thread8.start()
  elif key=="9":
     thread9 = Thread(target = play9)
     thread9.start()
  elif key=="v":
     ServoSleep(0)
     ServoSleep(1)
     ServoSleep(2)
  else:
   Stop()   
Standby()
ServoSleep(0)
ServoSleep(1)
ServoSleep(2)


BadPanda
Posts: 2
Joined: Sun Aug 09, 2015 6:20 pm

Re: Linux/C: Set baudrate before opening serial/UART; Servo

Thu Feb 18, 2016 1:47 pm

That is great stuff! Thanks for the code will be one similar shortly.
Have you thought to try this?

http://elinux.org/RPi-Cam-Web-Interface

It is a live stream I use when I just want to use my Pi as a webcam, I still use the regular RC controller and just need to make sure I am using my 5Ghz wifi instead of the 2.4Ghz that causes the RC to get bad signals.

Here is a video of me driving around my house from my upstairs office a few weeks back.

https://www.youtube.com/watch?v=VTV_FmEElDI

Return to “Interfacing (DSI, CSI, I2C, etc.)”

Who is online

Users browsing this forum: Bing [Bot] and 13 guests