1. Do not share user accounts! Any account that is shared by another person will be blocked and closed. This means: we will close not only the account that is shared, but also the main account of the user who uses another person's account. We have the ability to detect account sharing, so please do not try to cheat the system. This action will take place on 04/18/2023. Read all forum rules.
    Dismiss Notice
  2. For downloading SimTools plugins you need a Download Package. Get it with virtual coins that you receive for forum activity or Buy Download Package - We have a zero Spam tolerance so read our forum rules first.

    Buy Now a Download Plan!
  3. Do not try to cheat our system and do not post an unnecessary amount of useless posts only to earn credits here. We have a zero spam tolerance policy and this will cause a ban of your user account. Otherwise we wish you a pleasant stay here! Read the forum rules
  4. We have a few rules which you need to read and accept before posting anything here! Following these rules will keep the forum clean and your stay pleasant. Do not follow these rules can lead to permanent exclusion from this website: Read the forum rules.
    Are you a company? Read our company rules

Receving Serial with Arduino

Discussion in 'DIY Motion Simulator Projects' started by stowaway, Feb 7, 2012.

  1. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    I am receiving Serial numbers with my arduino now. ANd i have a LCD attached to print out the numbers.

    Im getting some funky Responses.. it seems to be perfect when i use the testplugin above the blue line, but when I go under it it gives me funky results (i think the results should be between 0-255) but they seem to be all over the shop..

    I decided to test the first character that was being sent and it was anything from ascii 48 - 55 (it should only be character 0,1,2 in my mind)

    are my results expected?

    basically the code im using is this:

    int getInt()
    {
    delay(2);
    int hundred = Serial.read();
    delay(2);
    int tens = Serial.read();
    delay(2);
    int ones= Serial.read();

    int i;
    i = ((hundred)-48)*100;
    i += ((tens)-48)*10;
    i += ((ones)-48)*1;

    return i;
    }

    Attached Files:

  2. eaorobbie

    eaorobbie Well-Known Member SimTools Developer Gold Contributor

    Joined:
    May 26, 2009
    Messages:
    2,574
    Occupation:
    CAD Detailer
    Location:
    Ellenbrook, Western Australia
    Balance:
    20,440Coins
    Ratings:
    +1,684 / 23 / -2
    My Motion Simulator:
    2DOF, DC motor, JRK, SimforceGT, 6DOF
    ok in ya math calc have you selected to use positive values or both positive and negative.
    Another is a timing issue with xsim, i use 20ms instead of 33ms as default in uso, plus as you increase the numberof values ya need to increase the timing allowance specially when using the lcd as for the inbuilt delay in lcd itself.
  3. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    Some how fixed it.. Not sure what i did. I didnt think i had split negative and postive values, but its workign now :)
  4. eaorobbie

    eaorobbie Well-Known Member SimTools Developer Gold Contributor

    Joined:
    May 26, 2009
    Messages:
    2,574
    Occupation:
    CAD Detailer
    Location:
    Ellenbrook, Western Australia
    Balance:
    20,440Coins
    Ratings:
    +1,684 / 23 / -2
    My Motion Simulator:
    2DOF, DC motor, JRK, SimforceGT, 6DOF
    ok ardinou and comport communication can be like a woman sometimes it work right and sometimes it needs a quick reset. Most of the time it works without failure
    good news.
  5. jyrki.j.koivisto

    jyrki.j.koivisto New Member

    Joined:
    Aug 30, 2010
    Messages:
    85
    Location:
    Finland
    Balance:
    314Coins
    Ratings:
    +3 / 0 / -0
    That is not the correct way to read serial data, you may miss some of it while delaying. 48 in decimal is 30 in hexadecimal and 0 in ASCII. You need to read the serial data in packet bases and then convert the ASCII data to integers for use in your code. ASCII is 8 bit wide and on your code you store it in INT (anything from 16 bits to whatever), those calculations take fare amount of time, there are better ways of converting ASCII to integers.

    There are also tons of other problems with that code, you can not know how big the value is coming from the serial before all of it is received, as it is send most significant digit first, there may not be any hundred digit at all, e.g 10
  6. eaorobbie

    eaorobbie Well-Known Member SimTools Developer Gold Contributor

    Joined:
    May 26, 2009
    Messages:
    2,574
    Occupation:
    CAD Detailer
    Location:
    Ellenbrook, Western Australia
    Balance:
    20,440Coins
    Ratings:
    +1,684 / 23 / -2
    My Motion Simulator:
    2DOF, DC motor, JRK, SimforceGT, 6DOF
    sorry for my miss guide.
    Like this is how i do it.

  7. jyrki.j.koivisto

    jyrki.j.koivisto New Member

    Joined:
    Aug 30, 2010
    Messages:
    85
    Location:
    Finland
    Balance:
    314Coins
    Ratings:
    +3 / 0 / -0
    You should make an interrupt routine that gets called whenever there is byte ready in the UART, check if you have a start of packet and set a boolean flag so next time when the interrupt comes you know you're filling in the data. Fill the data until end of packet is received or disgard the data if spurious start of packet is received or not a valid character (error recovery) Convert the ASCII data to integer with something else than multiplying it with 100 or 10, better yeat make the USO output hexadecimal values in ASCII and then convert them to integers.

    It's better to use hexadecimal inputs as that way you can get away without using multification instructions (slow) and instead just bit shift.
  8. BartS

    BartS Member

    Joined:
    Oct 10, 2010
    Messages:
    156
    Balance:
    417Coins
    Ratings:
    +5 / 1 / -0
    +1

    I agree with my friend Jyrki here, I know what hes talking about, thats the fastest most efficient way to do things. Interrupts free up the processor to do other stuff like bit banging and shifting. However this will also take you a while to grasp the concept of this operation.
    Much reading and learning is infront of you young padwan, this technique is certainly no overnight job for a beginner in Arduino.

    I suggest if you want to learn these techniques you dump the Arduino IDE and begin working with AVR studio or WINAVR to start then read many tutorials on bit shifting.

    BTW Jyrki Arduino IDE is not very good for control of usart as it is preconfigured in the Arduino core files at compile time, however it is still possible if you edit the core files.
  9. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    You've just increase my learning curb significantly.
  10. eaorobbie

    eaorobbie Well-Known Member SimTools Developer Gold Contributor

    Joined:
    May 26, 2009
    Messages:
    2,574
    Occupation:
    CAD Detailer
    Location:
    Ellenbrook, Western Australia
    Balance:
    20,440Coins
    Ratings:
    +1,684 / 23 / -2
    My Motion Simulator:
    2DOF, DC motor, JRK, SimforceGT, 6DOF
    well guys how about an example for us beginneer, always talking.
    My first play with an arduino was my dashboards and they work fine for what they are, no issues yet.
    But yes need to grasp the concept of interupts, so teach us, we are all ears.
    :tape:
  11. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    I have taken off the shackles of the Arduino IDE and gone down to the bare bones of winAVR.

    This code is just receiving Seiral input and echoing it, but it uses interrupts.

    How is this for a start to receive serial inputs from sender:

    Code:
    #include <avr/io.h>
    #include <util/delay.h>
    # include <avr/interrupt.h>
    
    #define F_CPU 16000000UL
    #define BAUD 9600
    #define MYUBRR F_CPU/16/BAUD-1
    
    
    void USART_Init( unsigned int ubrr)
    {
    	/*Set baud rate */
    	UBRR0H = (unsigned char)(ubrr>>8);
    	UBRR0L = (unsigned char)ubrr;
    	
      /*Enable receiver and transmitter */
    	UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    	
    	/* Set frame format: 8data, 2stop bit */
    	UCSR0C = (1<<USBS0)|(3<<UCSZ00);
    }
    
    void USART_Transmit( unsigned char data )
    {
    UDR0 = data;
    }
    
    
    unsigned char USART_Receive( void )
    {
    	return UDR0;
    }
    
    
    
    int main ( void )
    {
    	USART_Init(MYUBRR);
    	
    	/*Turn on Receive Complete Interrupt*/
    	UCSR0B |= (1 << RXCIE0);
    	
    	/*Turn On GLobal Interrupts*/
    	sei();
    	
    	
    	while (1)
    	{
    		
    		
    	
    	
    	}
    }
    
    
    ISR (USART_RX_vect) 
    {
    		char test = USART_Receive();		
    		USART_Transmit('b');		
    }
    
    
    
  12. jyrki.j.koivisto

    jyrki.j.koivisto New Member

    Joined:
    Aug 30, 2010
    Messages:
    85
    Location:
    Finland
    Balance:
    314Coins
    Ratings:
    +3 / 0 / -0
    Me now very happy! :) Welcome to the world of embedded devices. On x-sim USO interface when ever you transmit a value put a character at the beginning (something else than 0-9 or a-f(/A-F) if using just one start of packet char) for example z and for the end of packet (=EOP) put Z after the axis value.

    On your interrupt wait for the SOP (=start of packet) to appear and set some global flag to indicate that next time you will be taking the first char of the axis value in. After all chars of the axis is taken in you should receive the Z to indicate that everything has been decoded, now clear the global flag. While you take in the axis value chars decode the hex to integer at the same time.

    Here's a quick hack at the code, take it with a grain of salt though...

    Code:
    #define F_CPU 16000000UL
    #define BAUD 9600
    #define MYUBRR F_CPU/16/BAUD-1
    
    #include <avr/io.h>
    #include <util/delay.h>
    # include <avr/interrupt.h>
    
    int sync=0;
    int presync=0;
    
    #define axis0 1<<0
    #define axis1 1<<1
    #define axis2 1<<2
    #define axis3 1<<3
    #define axis4 1<<4
    #define axis5 1<<5
    #define axis6 1<<6
    #define axis7 1<<7
    
    int axis0val;
    int axis1val;
    int axis2val;
    int axis3val;
    
    void USART_Init( unsigned int ubrr) {
       /*Set baud rate */
       UBRR0H = (unsigned char)(ubrr>>8);
       UBRR0L = (unsigned char)ubrr;
       
      /*Enable receiver and transmitter */
       UCSR0B = (1<<RXEN0)|(1<<TXEN0);
       
       /* Set frame format: 8data, 2stop bit */
       UCSR0C = (1<<USBS0)|(3<<UCSZ00);
    }
    
    void USART_Transmit( unsigned char data ) {
    UDR0 = data;
    }
    
    
    unsigned char USART_Receive( void ) {
       return UDR0;
    }
    
    
    
    int main ( void ) {
    	USART_Init(MYUBRR);
    
    	/*Turn on Receive Complete Interrupt*/
    	UCSR0B |= (1 << RXCIE0);
    
    	/*Turn On GLobal Interrupts*/
    	sei();
    	
    	while (1) {
    	}
       
    }
    
    int htoi(char c) {
    	if(c >= '0' && c <='9') {
    		return (c - '0');
    	}else if(c>='a' && c <='f') {
    		return (c - 'A' + 10);
    	}else if(c>='A' && c <='F') {
    		return (c - 'A' + 10);
    	}
    	return -1;
    }
    
    ISR (USART_RX_vect) {
    	char c = USART_Receive();
    	int temp;  
    
    	if(sync!=0){
    		
    		temp=htoi(c);
    		if(temp==-1) {
    			sync=0;
    		}
    		
    		switch(sync) {
    			case axis0:
    				if(c!='Z'){
    					axis0val = (axis0val<<4) + temp;
    				}else{
    					sync=0;
    				}				
    			break;
    		  
    			case axis1:
    			break;
    		  
    			case axis2:
    			break;
    		  
    			case axis3:
    			break;
    		  
    			case axis4:
    			break;
    		  
    			case axis5:
    			break;
    		  
    			case axis6:
    			break;
    		  
    			case axis7:
    			break;
    		}
    	}else{
    		switch(presync) {
    			case 0:
    				if(c=='z') {
    					presync++;
    				}
    			break;
    			
    			case 1:
    				if(c>='0' && c<='7' ) {
    					sync = 1<<(c-'0');
    				}else{
    					presync=0;
    				}
    			break;
    		}
    	}	      
    }
    
    Basic idea is that USO sends z(axis number 0-7)(axis value in hex without 0x)Z for example z01234Z axis 0 with value 1234 in hex, there may be lots of errors in my code, I have not tested it in any way, some more error recovey might be good to add in there also.

    Edit1: Normally one would set the serial port as 8N1 (8 data bits, no parity and ONE stop bit) you have set it to two stop bits
    Edit2: Might be a good idea to actually set some flag when the value for one axis was decoded without errors, now there is no such error checking done
  13. BartS

    BartS Member

    Joined:
    Oct 10, 2010
    Messages:
    156
    Balance:
    417Coins
    Ratings:
    +5 / 1 / -0
    I do USART receive in a different way my axis coming from x-sim in binary format. I receive 2 bytes per axis and 6 axis = 12bytes this gives every axis a resolution of 0 - 65535 which is large enough for any smooth motion transitions.
    My packet arrangement has no start or stop chars its just barebones axis data for speed and efficiency, here is what it looks like.

    ~a01~~a02~~a03~~a04~~a05~~a06~
    X Y Z Xx Yy Zz

    Not having a start or stop char can cause problems and muddle up your axis information each loop, this is where my timing system kicks in.
    On each byte received I zero a timing variable in the USART ISR, that variable gets incremented every millisecond by my timer. I check in every USART Interrupt if my timing variable is below or over an elapsed period say 2ms. If period is greater than 2ms then I know it is a new packet and I flush my buffer before adding this byte, if less then 2ms it is still the same packet and I just keep adding to the buffer. I also check to see if there is 12 bytes in the buffer on each USART ISR if I have reached 12 bytes I then set a flag to parse my buffer in the main code into separate axis information. Each complete packet is received in 1.02ms at 115200 bps.

    Hope this helps, if you want to create something fast and efficient you really have to have your own creativity and think out of the box.

    Sorry for not giving any code examples as I dont have the time to put something together, however I also believe you will learn and gather valuble experience and understanding if you work it out for yourself. Anything that isnt working for you I wouldnt mind looking at explaining and correcting for you if you ask. But it goes against my policy to write give away code for free as I have worked hard and long at it. :cheers:
  14. jyrki.j.koivisto

    jyrki.j.koivisto New Member

    Joined:
    Aug 30, 2010
    Messages:
    85
    Location:
    Finland
    Balance:
    314Coins
    Ratings:
    +3 / 0 / -0
    I actually didn't know that USO outputs binary also... that simplifyes things as no ASCII to integer conversion is needed at all. There really is no reason to use timers when receiving data over serial. I might put together somesort of code example (probably for Microchip) as how to receive data
  15. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    I have been racking my brain. I have recieved the Binary from the USO (i think) and its in a char..

    So I dont know what to do with it now.. i cant dispaly it, because 1 would = 0000 0001
    so i dont know what to do with it... im not used to thinking on the bit level. (i will get used to it hopefuly)
  16. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    I like answering my own questions:

    unsigned char received = USART_Receive();
    unsigned char secondByte = USART_Receive();
    uint16_t val = (secondByte << 8) | received;

    :)
  17. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    So this is my unfinsihed, unworking code, so far.

    the axisinformation i am trying is R~a01~~a02~~a03~E
    16BIT in Binary format.

    essentially what it does is waits for a signal from serial.
    if the serial is R it keeps checking each expected axis (2 byte binarys) and then if it gets the correct amount of binarys and a E char at the end it outputs it to a LCD.

    Opinions are very very welcome :)


    Code:
    
    #include <avr/io.h>
    #include <stdio.h>
    #include <util/delay.h>
    #include <string.h>
    #include <avr/interrupt.h>
    #include lcd.h
    
    
    /* LCD DEFINES */
    #define LED PB5
    #define output_low(port,pin) port &= ~(1<<pin)
    #define output_high(port,pin) port |= (1<<pin)
    #define set_input(portdir,pin) portdir &= ~(1<<pin)
    #define set_output(portdir,pin) portdir |= (1<<pin)
    
    /* UART SERIAL DEFINES */
    #define F_CPU 16000000UL
    #define BAUD 9600
    #define MYUBRR F_CPU/16/BAUD-1
    
    
    #define STARTCHAR 'R'
    #define ENDCHAR 'E'
    
    char reading;
    char inputBuffer[5];
    char position;
    char output;
    int result;
    
    struct Axis
    {
    	uint8_t axisNumber;
        uint16_t position;	
    	uint16_t oldPosition;
    	
    } axis1, axis2, axis3;
    
    
    
    /* SETUP UART */
    
    void USART_Init( unsigned int ubrr)
    {
       /*Set baud rate */
       UBRR0H = (unsigned char)(ubrr>>8);
       UBRR0L = (unsigned char)ubrr;
       
      /*Enable receiver and transmitter */
       UCSR0B = (1<<RXEN0)|(1<<TXEN0);
       
       /* Set frame format: 8data, 2stop bit */
       UCSR0C = (1<<USBS0)|(3<<UCSZ00);
    }
    
    
    
    void USART_Transmit( unsigned char data )
    {
    UDR0 = data;
    }
    
    
    unsigned char USART_Receive( void )
    {
       return UDR0;
    }
    
    
    /****************/
    
    
    
    
    int main(void)
    {
    
    
    	/* INITALISE SERIAL */
    	USART_Init(MYUBRR);
       
       /*Turn on Receive Complete Interrupt*/
       UCSR0B |= (1 << RXCIE0);
       
       /*Turn On GLobal Interrupts*/
       sei();
       
       position = 0;
       
       
        /* Intalise LCD */
        lcd_init(LCD_DISP_ON);                /* initialize display, cursor off */
        lcd_clrscr();
    	lcd_puts(TEST); 
    		
    	while (1)                         /* loop forever */
    	{
    		
    		
    
    		
    	}
    }
    
    
    /* INTERRUPTS */
    
    ISR (USART_RX_vect)
    {
    
    
    	
    	unsigned char input = USART_Receive();
    	
    	if (input == 'R')
    	{
    	
    	//First Axis
    	_delay_ms(20);
        unsigned char firstByte = USART_Receive();      	
    	_delay_ms(20);
    	unsigned  char secondByte = USART_Receive();      	
    	_delay_ms(20);
    	
    	//Second Axis
    	_delay_ms(20);
        unsigned char thirdByte = USART_Receive();      	
    	_delay_ms(20);
    	unsigned  char forthByte = USART_Receive();      	
    	
    	//Third Axis
    	_delay_ms(20);
        unsigned char fithByte = USART_Receive();      	
    	_delay_ms(20);
    	unsigned  char sixthByte = USART_Receive();      		
    	_delay_ms(20);
    	
    	
    	unsigned  char endByte = USART_Receive();      	
    	
    	if (endByte == 'E')
    	{
    		//SUCESS
    		axis1.position = (firstByte << 8) | secondByte;	
    		axis2.position = (thirdByte << 8) | forthByte;	
    		axis3.position = (fithByte << 8) | sixthByte;	
    		
    		
    		char axis1Printout[12];
    		char axis2Printout[12];
    		char axis3Printout[12];
    		
    		sprintf(axis1Printout,%u , axis1.position);
    		sprintf(axis2Printout,%u , axis2.position);
    		//sprintf(axis3Printout,%u , axis3.position);
    		
    		char output[40] = ;
    		strcat(output, axis1Printout);
    		strcat(output, axis2Printout);
    		//strcat(output, axis3Printout);
    		
    		lcd_clrscr();     /* clear the screen*/
    		lcd_puts(axis1Printout); 
    	}
    
    
    	
    	}	
    
    }
    
    
  18. jyrki.j.koivisto

    jyrki.j.koivisto New Member

    Joined:
    Aug 30, 2010
    Messages:
    85
    Location:
    Finland
    Balance:
    314Coins
    Ratings:
    +3 / 0 / -0
    No, no, no, no....

    There is no need for delays in ISR, the interrupt routine gets called when ever there is something to be read from the UART. I'm not sure if Atmel has some sort of FIFO on UART but if not there is only one char to receive.
  19. jyrki.j.koivisto

    jyrki.j.koivisto New Member

    Joined:
    Aug 30, 2010
    Messages:
    85
    Location:
    Finland
    Balance:
    314Coins
    Ratings:
    +3 / 0 / -0
    I don't think there will be a need for a ring buffer with this sort of thing, it would only be needed if the transmitted data would be in more complex packet with checksums and such.

    One does not do ANYTHING else in the ISR routine than take the char out of UART (plus the logic for this simple packet system), no LCD driving while in the interrupt and no delays!

    First thing in the interrupt you should check if you have previously received the start of packet char, if not then check if the new char is the start of packet (R in your case) if it is then set a flag that a start of packet has been received and exit the interrupt. If the start of packet has been received then fill in the data, you get only one char, next time fill one char more until end of packet is received or you know that there will be no more data coming then clear the start of packet flag. The end of packet char can be disgarded and not used at all if you know exactly how much data there will be.

    Edit: While trying to sort out how I'm going to make my FD301 clone, I was wondering if there would be any interest in 2-3 axis controller board that drives external bridge board? I'm thinking to use Microchips dspic30f4012 as I have bunch of them laying around, It would most probably be smd board.
  20. stowaway

    stowaway New Member

    Joined:
    Mar 16, 2009
    Messages:
    213
    Location:
    Gold Coast - Australia
    Balance:
    1Coins
    Ratings:
    +0 / 0 / -0
    Thanks guys, Im learning as I go so its a huge help.

    I understand about how im using the Interrupts wrong and I have a better idea of how to proceed :)

    huge help

    ps- the LCD is just in there for debugging purposes. but I will move it out of the interrupt routine and just read from UART.