UDP to LCD serial, impossible to write 80 bytes using printf

12 Jan 2012

Hi guys,

I'm trying to get a project up that listens to UDP on port 4444 and transmits the payload to a serial LCD on P9 and P10.

Small packets go well, but if i try to send 80 bytes of stars "*" the LCD display shows part of the the 80 stars, and / or random garbage.

I presume the serial transmission get screwed up by a 10Ms timer that polls 14 switches.

Normally i would let the UDP receive dump the message in some fifo buffer, and let a seperate "thread" write it out to the serial.

Any suggestions on how to "ruggedize" the write to LCD?

:EDIT:

If you scroll down, you can follow my travels over hope and dispair to the final (working) solution.

Theo (NL)

08 Jan 2012

Do you use interrupts for the serial transmission ? Isn't your ethernet buffer overflowing or maybe your display can not handle so many bytes at once?

I my rom emulator I have interrupts every 2.5 usecs (!) and they last at least 1 usec and still the mbed pushes out debug printf's over the USB serial port at 115200 baud

08 Jan 2012

Hi Gert,

I'm, not using interrupts, i just declared a serial om P9 P10, and attached the LCD.

The LWIP lib is used for UDP, and the receive event is real simple :)

static void udp_lcd_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port) {

    led3 = !led3; //Blink led
   
    LWIP_UNUSED_ARG(arg);

    if (p != NULL) { /* if packet is valid */

        //Send payload to lcd
        lcd.printf("%s", static_cast<char *>(p->payload));

        /* free the pbuf */
        pbuf_free(p);
    }
}

But this is not safe, when you allso have a 10 ms timer going off for polling somtehing else.

Maybe write to a FIFO variable in the receive event, en have a seperate time for the serial writing ?

Theo

08 Jan 2012

Hi Theo, Does the printf formatting time take too long, when the payload is larger?

Maybe

Serial.putc(payloadCharacter_n)

would work faster, because no printf formatting is needed?

08 Jan 2012

Hi rod,

I tried your suggestion, but its no improvement. It must be a timing somewhere

        Len = p->len;
        strcpy(UdpIn, static_cast<char *>(p->payload)); 
        
        while (i < Len) {lcd.putc(UdpIn[i]); i ++;};

        /* free the pbuf */
        pbuf_free(p);

Trying to build a fifo now, so that the UDP recv event time is minimal.

Any suggestions on this ? Or for an other route ?

Theo.

08 Jan 2012

With the strcpy you are using an extra buffer already. The only thing I noticed: Is the string in the payload null terminated ? (You are taking an overflow risk in using strcpy instead of strncpy with a maximum size of your UdpIn buffer) What about sending the string to the debug terminal (USB serial) to make sure it is received OK.

08 Jan 2012

LCD modules with chip-driver such as Sunplus 780 (text module driver IC) have no buffer. The write cycle time is also quite long. Maybe the module is not able to keep up with the data from UDP. Do you know the chip type in the LCD, and the fastest cycle time?

08 Jan 2012

You might try to make the serial output unbuffered or even wait a fixed time after each character write.

09 Jan 2012

I will test your feedback tonight, all much appreciated....

My old C skills are coming back fast now :)

The Chip... not sure, it's a Matrix Orbital LK204-25 series, with a serial port.

http://www.matrixorbital.com/images/board/LK204-25_BACK.gif

http://www.matrixorbital.com/Intelligent-Display-Character-LCDs/c39_3/p25/LK204-25/product_info.html

09 Jan 2012

OK, that's a text LCD with a little 8-bit controller to provide Serial data reception, and driving of the module's commands & data.

We have to assume that the cycle time for LCD data writes is 1us, minimum. (the specification for at least one of the chips used in this sort of module). It would be safer to design for even slower, if possible.

Maybe the 8-bit controller provides 16-byte buffer, so that 16 writes are received OK. But then you must wait 16us (or 20us) before you write the next 16, to prevent overflow at the receive end.

09 Jan 2012

Hi Rod, Thanks for that!

Hmmm 16 - 20 us....

That means that i need to chop an UDP 80-100 byte write into max 16 Byte chunks with a little spacing. And if i do that the UART's hardware FIFO will handle it from there.

Ok, Got it.

One question remains though; i can break up an 80 byte char array in 5 chunks, no problem there.

But since i'm receiving data using UDP (100Mbit) and transmitting to LCD (115Kbit) i need a (FIFO) buffer of sorts to store the 80 byte UDP packet bursts, and have a timer on lets say 100us send the data out to the LCD in 16 byte chunks in a speed it can handle.

Is there piece of example code i can take a look at that recieves data on one callback routine, and uses a buffer to safely pass that data to an other routine for processing?

i.e.

How do you fill and empty a FIFO buffer safely in an MBED project, with interrups and timers going off?

Theo (NL)

09 Jan 2012

I just noticed in the data sheet the LK204-25 has extra pins for connecting a keypad:

Quote:

To facilitate user input, the LVK204-25 provides a Keypad Interface Connector which allows a matrix style keypad of up to twenty-five keys to be directly connected to the display module. Key presses are generated when a short is detected between a row and a column. When a key press is generated, a character specific to that key press is automatically sent on the Tx communication line.

Maybe you can use that instead of the keyboard functions you now use ?

09 Jan 2012

Hi Gert....

Well spotted!

That was my original design (and build), but the keyboard matrix of the LK is "somewhat" limited, since it's designed for 1 pushbutton at a time ONLY (source: customer support Matrix Orbital).

I need to handle 14 push and toggle buttons, of which 6 at a at any given time could be down or changing.

So, when that build proved unsuccessfull, i started looking for a microprocessor solution. I was thinking about arduino, but a colleague of mine suggested MBED, because of the integrated Ethernet, and the High clock freq.

I am building a prototype Flyball timing system based on MBED and Elexols EtherIO24:

http://dl.dropbox.com/u/43939704/Flyball%20interface%20aansluitschema%202012-en.jpg

http://dl.dropbox.com/u/43939704/Flyball%20course%201.jpg

http://dl.dropbox.com/u/43939704/flyball_ring_sm.jpg

If this works, it will become the standard for Europe, and i will publish it as a freeware design.

Im doing it for the betterment of the dog sport, because commercial timingsystems cost more then 15.000 euro's and most clubs can't afford that.

Theo (nl)

09 Jan 2012

Hi Theo,

Well, I see the complexity of your switch configuration, still it maybe could simplify some of the single function pushbuttons. On the other hand it is possible to detect multiple pushbuttons switches and/or toggle switches in a matrix configuration if you isolate the switches with diodes. It probably won't work with the LCD board but might simplify the mbed routines.

A principle explanation is found here (chapter 8):

http://www.dribin.org/dave/keyboard/one_html/

All in all it seems like a very nice project!

09 Jan 2012

Hi Gert,

No need to fiddle with the Keybuffer on the LCD, the MBED has plenty of ports and my poller works just fine.

It's just the UDP to serial thats buggin me :)

I think im going to solve it this way, since UDP is allmost a guarantee to loose a few data packets:

The app server will send 8 16 byte packets to the MBED, which will store them in 8 16 byte char arrays, then the app server will read back to verify the transfers, set a message length field and then toggle a complete flag.

The MBED waits for the complete flag, and then starts a 100us timer that will send 1 to 8 16 byte char arrays to the LCD in 100us intervals, based on the message length field.

This will ensure that the 8 16 byte char arrays in the app server are mirrored on the LCD, even if UDP packets gets lost along the way :)

Should i write out to the LCD in the mail loop:

while (1) { net->poll(); Check for network traffic if (CompleteLcdFlag) {WriteToLCD();}; }

Or give WriteToLCD() it's own independent timer?

What do you think ?

Theo (NL)

10 Jan 2012

Theo, Yes, you can copy the UDP payload to a character array. This can be done inside the UDP receive_callback (I have done this with 300byte packets of TCP, and it is reliable). Timers and other interrupts will not affect the copy or the serial-transfer.

You can use an mbed "Ticker" (repetitive interrupt) set to 100us.

This will be simple to write, and very reliable.

Rod

11 Jan 2012

Doesnt look to me that you need the Matrix Orbital IF board at all. With simple hardware you can drive the LCD directly from the mbed. Saves cost. Same might apply to the relaydrivers. A simple shiftreg and driver can control the relays. The mbed can use SPI to send data to the register.

11 Jan 2012

You are totally right Wim :)

But the original design was serial to the LCD, and use it's GPIO and Keyboard matrix.

Until i found i out i should better RTFM, and check if that keypad can still work with 4 keys permanently down :P

So, i cut my losses and moved to a MBED soluton, and since i had allready bought the serial displays i used them, and saved some pins for "future use"

The reason for having 4 microcontrollers, is that the main components are at least 10 and maximum 80 meters apart. So... serial @ 9600? nah... MBED and 100Mbit ... yeah !!!

I've come to first hate the MBED, then RTFM'ed some 25 year old school C and microprocessor books, and now i wonder why it was not my start anyways !

Grtz,

Theo (NL)

13 Jan 2012

Found it !

No MBED bug to blame, no LWIP to bash.... just myself and my limited insight in C. I even got a good hint from Gert.

ok.ok... here's the solution.

when you use this callback for the UDP receive event:

//UDP receive event
static void udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port) {

    led3 = !led3; //Blink led

    char UdpIn [1536] = ""; //Theoretical max size ethernet package, not counting jumbo ethernet frames.
    strcpy(UdpIn, static_cast<char *>(p->payload)); 

    //Debug (Do not leave on during runtime)
    int Len = 0;
    Len = strlen(UdpIn);
    printf("\n\r-----------------\n\r------RECEIVE---- LCD IP: %d.%d.%d.%d Port: %d, Len: %d, Payload:\n\r%s\n\r------END--------\n\r", ip4_addr1(addr), ip4_addr2(addr),ip4_addr3(addr), ip4_addr4(addr), port, Len, UdpIn);
    //Debug
    
    LWIP_UNUSED_ARG(arg);

    if (p != NULL) { /* if packet is valid */
        lcd.printf("%s", UdpIn);
        }
        pbuf_free(p); /* free the pbuf */
    }
}

And you see garbage behind the UDP data you sent to the MBED?

You forgot to attach a string terminator to the UDP payload your sending to the mbed!

//pseudocode in VB.NET
udp.send("whatever") --> causes buffer issues and gives garbage output

udp.send("whatever" + Chr(&H0))) --> no problemo !

:)

Theo (NL)

13 Jan 2012

Quote:

You forgot to attach a string terminator to the UDP payload your sending to the mbed!

Hehe.. :-) That is what I mentioned in one of the earlier messages:

Quote:

The only thing I noticed: Is the string in the payload null terminated ?

Great you got it working!

13 Jan 2012

Yep Gert... you did!

But i misread it for a hint about the LCD string.

Stupid me! /selfslap

:)