Is there a good tutorial on using FullDuplexSerial to receive data?
Robert Graham
Posts: 55
It's very simple to send data. And the setup instructions for the serial LCD screens make that very clear. I didn't have any trouble with that.
https://www.mouser.com/datasheet/2/321/27979-Parallax-Serial-LCDs-Product-Guide-v3.1-336825.pdf
But receiving data is another matter. Do I loop one of the methods to look for incoming data? What do I have to add to the Con section? Inquiring minds want to know.
This is code that everyone and their dog uses (if the dog has a propeller), and yet I can't google up a decent tutorial on how to use it. I wouldn't even know how to send with it, had I not purchased a serial screen years ago.
The simplicity of sending with FDS makes me think it won't be that difficult. Where is this info?
I just want to send a few dozen bytes between propellers. It would be nice if I could also send and receive strings. I saw on a another thread that FDS can't receive strings, so maybe I should look at another object?
As usual, love the hardware, but not the mysterious code that I don't understand. I am not a professional software engineer, and so it isn't easy for me to reverse engineer some one else's code.
I need to be able to send or receive at up to 40k baud, as MIDI runs at at about 31600. The devices need to be MIDI compatible eventually. I am not sure if Spin code is going to be able to handle that. But I have seen some threads where people have run FDS at 300k or faster. But again, can it receive strings?
Thank you in advance for any help!
https://www.mouser.com/datasheet/2/321/27979-Parallax-Serial-LCDs-Product-Guide-v3.1-336825.pdf
But receiving data is another matter. Do I loop one of the methods to look for incoming data? What do I have to add to the Con section? Inquiring minds want to know.
This is code that everyone and their dog uses (if the dog has a propeller), and yet I can't google up a decent tutorial on how to use it. I wouldn't even know how to send with it, had I not purchased a serial screen years ago.
The simplicity of sending with FDS makes me think it won't be that difficult. Where is this info?
I just want to send a few dozen bytes between propellers. It would be nice if I could also send and receive strings. I saw on a another thread that FDS can't receive strings, so maybe I should look at another object?
As usual, love the hardware, but not the mysterious code that I don't understand. I am not a professional software engineer, and so it isn't easy for me to reverse engineer some one else's code.
I need to be able to send or receive at up to 40k baud, as MIDI runs at at about 31600. The devices need to be MIDI compatible eventually. I am not sure if Spin code is going to be able to handle that. But I have seen some threads where people have run FDS at 300k or faster. But again, can it receive strings?
Thank you in advance for any help!
Comments
Yes, you do have to loop to receive the individual bytes until all of it is received. There is a built in receive buffer to avoid dropping bytes, but that can overflow if enough data comes in faster than your code can receive it. Using it for MIDI should not be a problem.
Again, you need to know what the data coming in looks like before you can deal with it. It can -- 40K baud is no problem. I wrote the code for a professional laser-tag controller that uses 57600 and process a lot of messages. In that case, I created a variant of FDS which provides a larger receive buffer so that I can queue messages and never miss them. In that case I'm using API-1 mode of the XBee which has a specific format.
There may be some strings in vendor specific SysEx data, but then you need to know the exact format.
Andy
See the file "midi_expander.spin" in the ZIP: https://forums.parallax.com/discussion/comment/799305/#Comment_799305
Here's the recieve method from Simple_Serial. It works, but has very limited application. That is to say, stick with FDS or one of its variants.
I do have a copy of Extended FDS, but I want to stick with the faster PASM code.
The device is basically a MIDI controller and a slave device that directly switches guitar pedals on and off that are (slightly) modified to plug right into it. And it also has audio signal path switching. However, it will be upgraded as time permits to have other functions. That's why we build things : )
I am going to study PASM so I don't have to do so much searching every time I need low level code. These things take time though.
What I really want to do, is spend my time developing features that no one else has done yet. It looks like the basic code for handling MIDI is available, but the ability to easily modify the patches with the controller it's self, I haven't seen that yet.
There is a very nice looking object on the OBEX called MidiIn that looks like it will handle all the difficult signal processing that I don't know how to do. I even found a web site that has a good tutorial on using it.
http://www.tymkrs.com/code/MidiInMe_Demo_1.spin
I really only need to turn channels off and on, so the signal processing is the hard part on the receive end.
I had been using my own crude code to send and receive signals. It's fine for sending a few dozen bits, but it's like trying to fight a war with rocks when it comes to sending bytes.
What I really need FDS for is for sending the full stack of saved patches back a forth between devices. I want to be able to easily back everything up without having to pull memory storage devices out of the units.
With the code you provided Jon, I should be able to get communication up in less than ten hours of labor : ) Hopefully much less. I am also going to check out your Nuts & Volts columns.
I'll check out midi_expander as well Ariba!
The only reason why this system is a difficult for me, is that it needs to be compatible with commercial devices. Because, I want to be able to plug commercial devices into my system.
Even sending data the whole stack of patches from one unit to another isn't really difficult. The just have to speak whatever gutter language I cook up... or borrow. But MIDI is a set standard. So I have to get this set up to standards.
I already have built and partially tested the old hardware that drives signal through the circular 5-pin cable. Even though it's supposed to work with 5 volt electronics, it's easy to adapt the official standard to work with 3.3 volt processors.
I haven't speced out how what MIDI messages I am going to use. However, it looks relatively easy. I found a good tutorial on Sparkfun
https://learn.sparkfun.com/tutorials/midi-tutorial/all.
I think I will save that part for later, though. First I need to get raw data sent and received with FDS.
https://forums.parallax.com/discussion/128443/newb-looking-to-make-a-midi-controller/p1
It looks like a good example of how to send MIDI code via FullDuplexSerial.
It is the RxCheck method that is the answer to your question... Look at the Spin code for fullDuplexSerial to see how the subsidiary methods for Rx and RxTime are built around RxCheck.
RxCheck pulls a character from the buffer (or returns -1 if nothing there) and does something with it, which might be to move it somewhere else (like a string buffer), or match it against a delimiter like ascii 13 or a comma or decimal point or a letter "M" or "m" (...), or it might take some action like hitting a drum if it receives a certain sequence, or it might test for a digit to build up into a decimal number or hex number or binary number, or it might just discard it, or it might wait until something comes in (Rx), or it might wait for a time and then bail out if nothing comes (RxTime). I draw it out just to emphasize what Jon said about there being infinite possibilities. The message is that it all centers around RxCheck and you don't really need to figure out the PASM behind it unless you really want to dig in.
In his case he's driving a synthesizer cog, but you can do anything you want once you've captured the data.
You know, Dee Snyder, defender of Metal. Vocalist for Twisted Sister.
Hey, I'm a guitar play. It's my nature.
Anyway, I need MIDI compatibility to be able to control modelers and make my looper compatible with other people's MIDI controller. People think of MIDI as being just for making synth music, but it is actually used for controlling all sorts of musical electronics. Basically, it allows me to make custom guitar effects loops that I can switch with one button. No tap dancing. Just, making The Metal!
However, if I get time, I WILL make synthesizers METAL. Trent Reznor did it on Broken. But, I digress. For now, I just need to get this thing up an running.
This is, if I am not insane, the very codes that will need to be sent to FullDuplexSerial on the send side:
$nc, $kk, $vv
Where:
$ indicates hexadecimal in Spin code. Other languages might use 0x or whatever....
n is the command (note on ($90) or off($80))
c is one of channels 1 thru 16, with each channel being a different device. Since this is all in hex, off on channel ten would be $9A
But I am assuming for now that my device will be on channel zero.
kk is the key number (0 to 127, where middle C is key number 60). So transmit something from $00 to $7F. Each key number represents either a relay or a stomp box that will be switched. My rig probably won't have that many devices to switch, but I don't mind having so much breathing room.
vv is the striking velocity (0 to 127) This has no use in binary switching. So just transmit a fixed value of $40.
So, a message for switching on stomp boxes two and four would look like this:
$90, $02, $40
$90, $04, $40
It's overkill to use MIDI like this, but again, this is all about compatibility. And, it should be very simple to program all of the necessary codes into the controller I have built. The user need not ever see them, they are just sent as needed. In the GUI, he/she enters a 1 or a 0 for what they want on or off, and the machine sends whatever it needs to send. Actually, I think this is going to be a necessity for preventing insanity.
Anyone would prefer having to enter "10010000" for a patch that switches eight devices on channel zero to this:
$80, $00, $40, $90, $01, $40, $80, $02, $40, $90, $03, $40, $80, $04, $40, $80, $05, $40, $80, $06, $40, $80, $07, $40, $80, $08, $40, $80, $09, $40
The latter is easy to generate via code.
I don't think it would be necessary to send a code for every "note", though. Just the ones I want the hub to switch on. So:
$90, $01, $40, $90, $03, $40 The hub turn everything off that it wasn't just told to turn on.
I already have an patch editor coded in Spin, so that should be a good starting point for this new system.
The interesting part for editing and displaying the info on the patches, will be figuring out what sort of management is going to be needed for other people's devices. I plan on using this to switch a V-Amp3, probably a V-Amp2, and also a HD500x.
And, it should be as flexible as possible for other uses. So, that will be a fun challenge.
I'm a big fan of writing small, atomic methods that are easy to integrate -- even with each other (see our Note On with velocity of 0 calls Note Off). These methods assume that the parameters passed are well-behaved. For example, if you sent a channel value of 16 to any of these methods, you'd end up changing the command.
Things become interesting if you choose to support "running status." To do that you'd have to save the last status byte and filter it from subsequent transmissions. The final complication -- as I've been reading -- is the Explicit Note Off message where there is no velocity byte. In the MIDI receiver I'm working on I have a setting that allows for this (needs to be tested).
Yeah, I'm trying to do that. It seems like I cannot get that in the first draft; it tends to be the third revision that is configured in nice neat methods.
My more complicated stuff looks like this:
That is the editor that is used to enter/change the name of the patch, the binary settings (because most of the data is just for switching things on and off), changing the settings that are integers (such as which bank number I want the modeler on), and it also sends data to an LCD screen driver. This version sends data to HugeLcd.Driver. So, tricks need to be employed so that the proper ASCII2 code is sent.
Nothing conceptually difficult has to happen on this end to make it work. Ergonomics, on the other hand, are difficult to design. How much information should be displayed on the screen? It's one of those large blue ones you could get from Sparkfun:
https://www.sparkfun.com/products/retired/8799
No longer available, btw
One of the nice things about the driver I am using for this unit, is the ability to display large or small characters. The large characters are easy to read from far away, but you only get five lines with those. Sixteen lines of small characters will fit, though.
There is no way the actual data that would be sent would all fit on the screen. So, some kind of shorthand will be necessary. This shouldn't be a problem. The software already does a lot of things to make the ASCII2 codes that are needed for the display. Making MIDI codes to sent will just be a matter of writing a bit more code.
Metallica <--- this is the name line. It can be just about any ASCII2 code. Up to ten characters, as this is the large text line.
1 4 8 <--- binary data line one. It's actually storing either a one or a zero, and displaying various numerals for clarity.
2 <--- binary data line two. Works just like binary data line one.
BAC <--- this is the integer line. It displays letters to save space. Each space (there are eight total) can be from A to Y.
The current version uses large characters to display everything. I think I am going to have to go to small character for everything but the name line. That way, the binary data can be displayed on one line (it will fit 20 small characters on a line), and I will have eleven others for the MIDI settings.
But, how does the MIDI data need to be displayed?
Perhaps:
Metallica <--- pretend these are really big characters.
A D H J P <--- singular binary data line. I would use letters so I can stick to single small characters. This is for my hub.
Vamp3 | 91,03,40 <--- A short name, and a MIDI code per line.
HD500x | 92,14,40
Keyboard| 93,08,40 <--- Or you just keep entering codes. Somehow the machine will need to be told what type of data is in each slot (2 per line)
93,15,50 |93,1E,60 <--- I have no idea what actual codes I will be sending, but it might be a lot. So I think it needs to be an unlimited number
of messages per patch. This should be doable. I use 64K EEPROM's, and the whole upper half is used for storing
variables. Start bits and end bits (or bytes) would be needed.
Unless you use fastspin (in which case your spin gets turned into PASM and you can write inline PASM to go with it).
I think I need to remind people that not everyone on the forums is a professional software engineer.
And yes, it is worthwhile to take the time to see how things work.
Having said that, we all only have limited time to get things done. So, tutorials are a good thing.
Why? FullDuplexSerial works well and the Spin routines are reasonably straight forward. Pretty much everything you need to send and receive 8 bit bytes in multiple formats is there. Study the Spin "Pub" routines and add some comments to them and you should have a pretty good handle on serial data.
rx/tx receive/send 8 bits (1 byte) of data
str uses tx to send a specified number of bytes of data
hex uses tx to send a byte as two hexadecimal characters (0 - 9, A, B, C, D, E, F)
etc. etc. etc.
You can add Spin "Pub" routines to format the data in other ways. For instance I have added routines to receive strings of data terminated by a specified character/byte. It is mainly used to get a number terminated by a "cr", but since I can specify the terminating byte it can be used for other things.
That's what I am doing now. I didn't really study the operators in depth when I did the front end of my one big Propeller project. So, now I am look at lines like this and scratching my head:
repeat while (rxbyte := rxcheck) < 0
You probably recognize that from the rx method of FDS. It's not setting rxbyte equal to rxcheck, is it? What exactly is it doing? I have been looking through the manual to find what other syntax := is used for.
I had a decent front end, and now I am trying to bring the back end up to standards. This has to be done in less than 80 hours. Or I have to cancel the upgrade.
So the full statement:
repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms
Translates to:
Repeat until
(the value returned by the rxcheck method is equal or greater than 0)
or
((cnt - t) / (clkfreq / 1000)) is greater than ms
t is the value of the cnt register before entering the repeat loop
(clkfreq / 1000) is the number of clock cycles in one ms
(cnt - t) is the number of cycles that have passed since the repeat loop was entered
So basically repeat until a byte is returned or the specified no of milliseconds have passed.
Yes, Rx starts off, It is among the Propeller's assignment operators, which have the following property, from the Prop manual 1v2 (table on pages 144-145, and following text):
Those that are assignment operators, however, write their result to either the variable they operated on (unary), or to the variable to their immediate left (binary), in addition to providing the result for use by the rest of the expression. .
Study the table of operators charfully!
So the statement is doing two things, assigning whatever is returned by RxCheck to the variable rxbyte, and then checking to see if rxbyte is less than zero. RxCheck returns a value -1 if nothing is in the serial queue. Consequently the Rx method simply sits there until real ascii character with a value of 0 to 255 appears is received. If something is received, the application program will usually want to do something with it, good, there it is in rxbyte, which is returned to the calling program as the result of Rx to use as it sees fit.
The following abbreviated form, would also stick in the repeat until something is received, and that might be fine if all you want to do, say, is to detect if a user has press any key. But it won't do you any good if you need to do something with the received value, because you can't use RxCheck again to capture the same character. The next RxCheck points to a new position in the queue, another character received or nothing.
The example that Kwinn mentioned is for RxTime, which adds a timeout to the process, which is useful, say, if you want to give a user x seconds to press a key, but move on if not. If it does receive something before the timeout, it is passed on to the caller, or -1 if nothing or timeout.
In application code you will often see something like this, with an if instead of a repeat That would usually part of a larger loop that occasionally checks to see if there is a character ready in the buffer. The calling program can pick up a command or whatever when one happens to arrive, but otherwise manage other tasks.
I am probably going to need to be sending strings. So, I think I am going to use RxCheck to detect the head of the message, and then loop Rx to capture the rest.
I've been trying to get the send side partially done, but my old spaghetti code is just about clogging up the Propeller chip.
This locks the system up: I don't know why exactly it does this, but every time I loop a call like the above, the Prop locks up. I should have gotten on adopting a respectable com protocol earlier, and not waited until I needed it.
I am going to try to use this instead:
I probably should have been sending strings like that all along. But, it wasn't easy to find out how to send an array as a string like that. I replaced this display code: with this much neater code that seems to be working quite well: I am thinking Is much superior to This is my homebrew code for sending data: The only good things about the above is that doesn't take a lot of resources, and it is easy to modify. It is incapable of sending bytes in any convenient format. However, I DIDN'T NEED TO SEND BYTES. So, it was fine in the past. Basically, the device only flipped relays on and off.
The problem I face now, is modifying it so that it can use MIDI code.
This is easy on the receiving end, because that side has 6600 longs free.
It's the sending side that I am worried about, because it keeps locking up whenever I try to include a complicated upgrade. I am going to have to simplify it's code, and maybe eliminate some features that I won't be using much. Or I will have to go to a dual boot system.
Is it locking up because it only had 3600 longs free? Maybe it's due to my liberal use of global variables?
I am going to keep trying to make the software send some kind code with FullDuplexSerial. I will make an entire custom test object if I have to, but I want to keep under my 80 hour time budget for this upgrade.
FDS.str(@e) sends the bytes from the e[] array until a zero-byte is reached, while your repeat code sends the first 19 bytes in e[] independent of the value of the bytes (also zeroes).
FDS.str($7E) sends the bytes from address $007E in hubmemory, i don't think this is what you want.
FDS.hex($7E,2) sends 2 characters '7' and 'E' as ASCII codes, maybe you want that, FDS.tx($7E) sends a single byte with value $7E, I think this is what you need for MIDI messages.
Andy
I have no idea where the addresses of the variables are (yet), so FDS.str($7E) isn't at all right. Maybe if I knew how to aim....
I thought Tx would only handle one character for some reason. I guess I just haven't gotten my head wrapped around hexadecimal yet. I'm working on it.
I was trying to send bytes in hexadecimal (which is what MIDI is built on), so FDS.tx($7E) is what I probably need for sending MIDI.
I still want to get some variation on this working though: ...for the simple fact that it is so convenient to just be able to send a one or a zero to tell the receiving end what to do with the relays.
I might just have to build a whole new interface around FullDuplex serial.
If you want to have access to the CMAP to work with it, use CMap Tools.