United Kingdom
Asked — Edited
Resolved Resolved by ptp!

Sdk Uart Question

Hi i am having problems using the Uart 0 with the sdk c# on my ez-b v4 i am writing a standalone app as well as a plugin for my inmoov bot, as i only have one ez-b v4 (limited funds) i am using an stm32F429 discovery board i had lying around to operate the additional servos and neopixel ring. The F429 disco board is working fine and i can send a simple command from the ez-b the protocol is just 4 bytes [0xFE][command byte][servonum byte][value byte] and works fine at 115200 F429 recieves the commands without any problems but i then send back either the same 4 bytes to acknowledge a set command or with a changed value byte if requesting a value from the F429 i have the following code set up in C# to wait for the response but it always returns the same value of 239 0xef in both my standalone version and the plugin version the F429 is sending correctly as i have connected it to an arduino , Nulceo stm32F746 and Nucleo stm32F401 and they all receive the 4 bytes correctly. i suspect i am doing something wrong as the _ezb.Uart.UARTExpansionAvailableBytes always seems to return 4086 once i send it data from the F429 I have been struggling with this for a couple of days now so any help would be appreciated am i using the right command to read the data?

private byte[] waitforresponse(int port) { // start timeout timer timeoutTimer.Enabled = true; TimedOut = false; // here to debug uart rx problem // bytes = 0 first time though the loop int bytes = _ezb.Uart.UARTExpansionAvailableBytes(port); while ((_ezb.Uart.UARTExpansionAvailableBytes(port) < 4) && (TimedOut == false)) {

            Application.DoEvents();
        }
        timeoutTimer.Enabled = false;
        // here to debug uart rx problem
        // bytes = 0 first time though the loop

        bytes = _ezb.Uart.UARTExpansionAvailableBytes(port);
        if (TimedOut == false)
        {
            // original code return 4 bytes all with 239 0xEF
            //byte[] data = _ezb.Uart.UARTExpansionRead(port, 4);
           
            // added this for debug 
            // reads 4086 bytes all of the same value 239 0xEF
            byte[] data = _ezb.Uart.UARTExpansionReadAvailable(port);
            return data;
        }
        else
        {
            return null;
        }
    }


ARC Pro

Upgrade to ARC Pro

Harnessing the power of ARC Pro, your robot can be more than just a simple automated machine.

PRO
USA
#1  

@paulbearne04,

if you have a remote device connected (uart) sending bytes, EZ-B will process the bytes from the UART FIFO and will keep it in a fixed buffer .

buffer size = 5000 bytes https://synthiam.com/Community/Questions/5403

If you don't execute UARTExpansionRead or UARTExpansionReadAvailable the data will accumulate in the buffer, and then buffer will overflow (losing old data or not keeping up with the new data).

Can you check the remote side logic if is not generating the data, while waiting for an ACK (your software logic) from the EZB side ?

Can you post the Arduino/STM32 code ?

United Kingdom
#2  

Thanks for the reply ptp

That's what i put the 4086 bytes available down to but the code is break pointed at remote and in VS so could only ever send once , my thoughts also as the number it shows its buffer full of it 0xEF and my remote which is nibbles reversed of the sof character sent at the start of the data packet 0xFE which i think may just be a red heron also when i collect the data and look at it using UARTExpansionReadAvailable it is just full of 0xEF. I can see on the lcd the command is received only once thats not to say that MBED code is not messed up lower down but the keil debugger stops the code when a breakpoint is hit so i'm pretty certain it only gets there once. while typeing i realised i haven't tried just sending a byte from the remote so will try that later

code for remote

is pretty simple Serial device(PC_10,PC_11);

void ParseCommand() { uint8_t Text[30]; uint8_t txbuf[4] = {buf[0],buf[1],buf[2],buf[3]}; int i;

lcd.ClearStringLine(LINE(currentCommandLine));
led1 = !led1;
switch (buf[1]) {
    case 0x00:
        break;
    case 0x01:
        sprintf((char*)&amp;Text,&quot;Move servo %d to %d&quot;,(int)buf[2],(int)buf[3]);
        lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text, 
       CENTER_MODE);
        moveServo(buf[2],buf[3]);
        break;
    case 0x02:
        sprintf((char*)&amp;Text,&quot;Set servo %d Min to %d&quot;,(int)buf[2],(int)buf[3]);
        lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text, 
                         CENTER_MODE);
        servomins[buf[2]] = buf[3];
        break;
    case 0x03:
        sprintf((char*)&amp;Text,&quot;Set servo %d Max to %d&quot;,(int)buf[2],(int)buf[3]);
        lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text, 
                      CENTER_MODE);
        servomaxs[buf[2]] = buf[3];
        break;
    case 0x04:
        sprintf((char*)&amp;Text,&quot;Set servo %d speed to %d&quot;,(int)buf[2],(int)buf[3]);
        lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text, 
               CENTER_MODE);
        servospeeds[buf[2]] = buf[3];
        break;
    case 0x05:
        break;
    case 0x06:
        sprintf((char*)&amp;Text,&quot;Set servo %d steps to %d&quot;,(int)buf[2],(int)buf[3]);
        lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text, 
                          CENTER_MODE);
        servosteps[buf[2]] = buf[3];
        
        break;
    case 0x07: // is moving command
          sprintf((char*)&amp;Text,&quot;Get servo %d moving state&quot;,(int)buf[2]);
          lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text, 
                CENTER_MODE);
          if (servomoving[buf[2]] == true){
             txbuf[3] = 0x01;
           } else {
               txbuf[3] = 0x00;
           }
           
           break;
    case 0x08: // get current position
          sprintf((char*)&amp;Text,&quot;Get servo %d Position&quot;,(int)buf[2]);
          lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text, 
                                                 CENTER_MODE);
          txbuf[3] = currentservopos[buf[2]];
          break;       
    case 0x09:
        break;
    case 0x10 :
        break;
}
if (currentCommandLine &gt; 9)
{
    currentCommandLine = 6;
}
// send response
for(i=0;i&lt;4 ; i++)
{
  
  device.putc(txbuf[i]);
}

}

// serial uart Rx ISR void callback() { uint8_t inchar; // while the fifo has data loop collecting it while(device.readable()) { // read the first character this will also clear the interupt inchar = device.getc(); // see if we have sof character if( inchar == 0xfe) { buf[0] = 0xfe; startbyte = true; bufcount = 1; } else { if (startbyte) { // received a sof so start getting command and data buf[bufcount++] = inchar; if (bufcount >= 4) { bufcount=0; // we have full command packet so act on it ParseCommand(); // reset sof received flag ready for next command startbyte = false; } }

    }
}

}

PRO
USA
#3  

@paulbearne04,

Use code tags e.g. [ code ] and [ /code ] to format the code:

I added some code to help the troubleshooting:


void ParseCommand()
{
        //added by ptp
	static int unknown_commands = 0;
        
	uint8_t Text[30];
	uint8_t txbuf[4] = { buf[0], buf[1], buf[2], buf[3] };
	int i;

	lcd.ClearStringLine(LINE(currentCommandLine));
	led1 = !led1;
	switch (buf[1]) {
	case 0x00:
		break;
	case 0x01:
		sprintf((char*)&amp;Text, &quot;Move servo %d to %d&quot;, (int)buf[2], (int)buf[3]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		moveServo(buf[2], buf[3]);
		break;
	case 0x02:
		sprintf((char*)&amp;Text, &quot;Set servo %d Min to %d&quot;, (int)buf[2], (int)buf[3]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		servomins[buf[2]] = buf[3];
		break;
	case 0x03:
		sprintf((char*)&amp;Text, &quot;Set servo %d Max to %d&quot;, (int)buf[2], (int)buf[3]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		servomaxs[buf[2]] = buf[3];
		break;
	case 0x04:
		sprintf((char*)&amp;Text, &quot;Set servo %d speed to %d&quot;, (int)buf[2], (int)buf[3]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		servospeeds[buf[2]] = buf[3];
		break;
	case 0x05:
		break;
	case 0x06:
		sprintf((char*)&amp;Text, &quot;Set servo %d steps to %d&quot;, (int)buf[2], (int)buf[3]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		servosteps[buf[2]] = buf[3];

		break;
	case 0x07: // is moving command
		sprintf((char*)&amp;Text, &quot;Get servo %d moving state&quot;, (int)buf[2]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		if (servomoving[buf[2]] == true){
			txbuf[3] = 0x01;
		}
		else {
			txbuf[3] = 0x00;
		}

		break;
	case 0x08: // get current position
		sprintf((char*)&amp;Text, &quot;Get servo %d Position&quot;, (int)buf[2]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		txbuf[3] = currentservopos[buf[2]];
		break;
	// ************* PTP : Changes : begin ********************
	//case 0x09:
	//	break;
	//case 0x10:
	//	break;
	default:
		sprintf((char*)&amp;Text, &quot;Garbage: cnt=%d cmd=%d&quot;, ++unknown_commands, (int)buf[1]);
		lcd.DisplayStringAt(0, LINE(currentCommandLine++), (uint8_t *)Text,
			CENTER_MODE);
		break;
	}
	// ************* PTP : Changes : end ********************
	if (currentCommandLine &gt; 9)
	{
		currentCommandLine = 6;
	}
	// send response
	for (i = 0; i&lt;4; i++)
	{
		device.putc(txbuf[i]);
	}
}

// serial uart Rx ISR
void callback()
{
	uint8_t inchar;
	// while the fifo has data loop collecting it
	while (device.readable()) {
		// read the first character this will also clear the interupt 
		inchar = device.getc();
		// see if we have sof character
		if (inchar == 0xfe) {
			buf[0] = 0xfe;
			startbyte = true;
			bufcount = 1;
		}
		else {
			if (startbyte) {
				// received a sof so start getting command and data
				buf[bufcount++] = inchar;
				if (bufcount &gt;= 4) {
					bufcount = 0;
					// we have full command packet so act on it 
					ParseCommand();
					// reset sof received flag ready for next command
					startbyte = false;
				}
			}

		}
	}

}

Let's check simple things in sequence:

  1. 3 wires to connect both devices: rx, tx, gnd

  2. you have somewhere serial format & baud: device.format(8, Serial::None, 1); device.baud(115200); Try to change to a slow speed e.g. 9600

  3. Somewhere in your c# code you have: UARTExpansionInit(0, 115200);

  4. Your c# code does the following: connects to the EZB wait a few seconds (or connect event) executes UARTExpansionInit (... ) executes UARTExpansionReadAvailable (...)

  5. if you call UARTExpansionAvailableBytes(...) what is the return value ?

  6. do you get any feedback in the LCD after the above points?

  7. Send a command or dummy command, does the LCD reports the command ?

  8. if you execute UARTExpansionAvailableBytes (...) do you get 4 bytes reported ?

United Kingdom
#4  

Thanks Ptp i'll add the code and i should of put default case tomorrow pm as evening here now and have a hospital appiontment tomorrow am. Thanks for tip on the tags

i have tried 1 2 and 3 , 4 i use onconnect but do init later when i'm using the comms 5 it reads 0 on first call then 4086 after that. 7 lcd does report the command being received one the functions in c# code is to send servo config min max speed and step which get reported correctly on the lcd so i dont think its a timing issue. i'll check 8 as at the moment it will come out of the loop on timeout or any number of characters as long as its above 4.

PRO
Synthiam
#5  

PTP is rock'n the question - however, i have a question about why you're using Application.DoEvents()?

United Kingdom
#6  

Hi DJ Sures yes just yielding to allow other parts of the app to update display etc if for any reason the Comms response doesn't arrive as it will wait in the while loop for the timeout to occur and the the app got a bit jerky if comms not connected.

PRO
USA
#7  

@paulbearne04,

two questions:

  1. Board model connected to the EZB
  2. Can you post the Serial pinout initialization code: e.g. Serial device(D10, D2);
PRO
USA
#8  

@paulbearne04,

I created a sample mbed program to test the serial communication between a STM32F01 and EZB v4.

the message protocol is simple and you can use ARC's Serial Terminal to test the interaction.


protocol description:
msg start char: &lt;
msg end char: &gt;
msg delimeter: space

examples:
ezb send &quot;&lt;hello&gt;&quot; 
stm32 returns &quot;&lt;hello im_ready&gt;&quot;

ezb send &quot;&lt;sum 10 20 30&gt;&quot;
stm32 returns &quot;&lt;sum_result 60&gt;

ezb sends an unknown command e.g. &quot;&lt;abc&gt;&quot;
stm32 returns &quot;&lt;error unknown_command abc&gt;

if the communication is idle for more than 15 seconds:
stm32 sends &lt;DEBUG debug_counter=nn rx_counter=nn rx_start_byte=nn msg_ready=nn&gt;

the code is simple, no bullet proof neither is rocket science:) i hope it can help.


#include &quot;mbed.h&quot;
#include &lt;ctype.h&gt;

#define MAX_RX_TOKENS 10
#define MAX_RX_BUFFER 128
#define MAX_TX_BUFFER 128

volatile uint8_t rx_counter = 0; 
volatile uint8_t rx_start_byte = false;
volatile uint8_t msg_ready = false;
char rx_buffer[MAX_RX_BUFFER + 1];
char tx_buffer[MAX_TX_BUFFER + 1];
char* rx_tokens[MAX_RX_TOKENS];

Serial serial(PA_9, PA_10);  // serial1
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);

void process_msg()
{
	int number_of_tokens = 0;
	rx_tokens[number_of_tokens++] = strtok(rx_buffer, &quot; &quot; );
	while ((rx_tokens[number_of_tokens] = strtok(NULL, &quot; &quot; )) != NULL) { number_of_tokens++; }

	if (strcmp(rx_tokens[0], &quot;hello&quot; ) == 0)
	{
		serial.printf(&quot;&lt;hello im_ready&gt;&quot; );
		return;
	}

	if (strcmp(rx_tokens[0], &quot;sum&quot; ) == 0)
	{
		int sum;
		for (int i = 1; i &lt; number_of_tokens; i++)
		{
			sum += atoi(rx_tokens[i]);
		}
		serial.printf(&quot;&lt;sum_result %d&gt;&quot;, sum);
		return;
	}

	serial.printf(&quot;&lt;error unknown_command %s&gt;&quot;, rx_tokens[0]);
}

void tx_callback()
{
	led2 = !led2;
}

void rx_callback()
{
	led3 = !led3;

	while (serial.readable())
	{
		char inchar = serial.getc();
		if (inchar == '&lt;' )
		{
			msg_ready = false;
			rx_start_byte = true;
			rx_counter = 0;
		}
		else if (rx_start_byte)
		{
			if (inchar == '&gt;' )
			{
				rx_buffer[rx_counter] = 0;
				msg_ready = true;
				rx_start_byte = false;
			}
			else if (rx_counter &lt; MAX_RX_BUFFER)
			{
				rx_buffer[rx_counter++] = inchar;
			}
		}
	}
}

int main()
{
	serial.baud(115200);
	serial.attach(&amp;rx_callback, Serial::RxIrq);
	serial.attach(&amp;tx_callback, Serial::TxIrq);

	Timer debug_timer;
	debug_timer.start();

	uint16_t debug_counter = 0;

	while (1)
	{
		if (msg_ready)
		{
			process_msg();
			msg_ready = false;
			debug_timer.reset();
		}

		//if idle for more than 15 seconds send a debug msg
		if (t.read_ms() &gt;= 15000)
		{
			serial.printf(&quot;&lt;DEBUG debug_counter=%d rx_counter=%d rx_start_byte=%d msg_ready=%d&gt;&quot;, ++debug_counter, rx_counter, rx_start_byte, msg_ready);
			debug_timer.reset();
		}
	}
}

United Kingdom
#9  

Hi Ptp unfortunatley things didn't go according plan at the hospital so haven't been able to look at it at all today. thanks for the code i'll put it into my 401 tomorrow.

United Kingdom
#10  

sorry forgot to answer your questions i tried my code in nucleo f407 , discovery f429 and nucleo f746 i'll put the init code up tomorrow for each

PRO
Synthiam
#11  
  1. Is this a stand-alone app using the SDK or an ARC skill plugin?

  2. Do not ever ever ever use Application.DoEvents. Your code may be using a System.Windows.Forms.Timer instead of System.Timers.Timer.

Use the System.Timers.Timer, which will run in a separate thread. However, beware that you will need to use an Invoke for a System.Timers.Timer thread to modify any GUI Application thread objects

United Kingdom
#12  

Hi DJ the standalone app is using the SDK still learning c# more used to c Timer changed and doevents disposed of to use system timer.

United Kingdom
#13  

Thanks Ptp for all your help. i took your code and built it in the Mbed online compiler and worked as advertised (slight typo ) which i corrected uploaded the bin file and worked great. i then exported it from Mbed online to Keil and same horror story so went back to my code this time using the online compiler and it worked without the error. after a bit of investigation and numerous exports i tracked it down when the exported project is exported it enables optimisation level 3 in one of the command line compiler options set it to level 0 or disabled and everything works not sure why they turn optimisation on as it never works and cause more problems than its worth. Many thanks again for your help. And thanks DJ for your rip with the timer. I'll post the app and plugin once they are finished i've got all the servo mapping done now just need the last few parts to finish printing and i can put the functionality in.