Problem with serial comms in FDS
I am communicating with a coin hopper. Seems rather simple- you send it a serial command, it responds with a message and dispenses coins.
In my routine I reset the communication message counter, pause then issue a coin dispense command for 1 coin, pause then issue a second coin dispense command for 4 coins.
For some reason my code won't receive the response from the hopper the second time through the dispense routine. I can see it on the logic analyzer and it looks exactly as expected. I've tried putting rxflush in several different spots and using rxtime or just rx but to no avail. It dispenses the coins fine and as I said the analyzer shows the response every time.
Here's the screen shot of PST:

Here's my code:
Any suggestions?
In my routine I reset the communication message counter, pause then issue a coin dispense command for 1 coin, pause then issue a second coin dispense command for 4 coins.
For some reason my code won't receive the response from the hopper the second time through the dispense routine. I can see it on the logic analyzer and it looks exactly as expected. I've tried putting rxflush in several different spots and using rxtime or just rx but to no avail. It dispenses the coins fine and as I said the analyzer shows the response every time.
Here's the screen shot of PST:
Here's my code:
con
_clkmode = xtal1 + pll16x ' Feedback and PLL multiplier
_xinfreq = 5_000_000 ' External oscillator = 5 MHz
MS_001 = 80_000_000 / 1_000 ' ticks in 1ms
HOPRX = 0 ' to pin 6 on Hopper
HOPTX = 1 ' to pin 5 on Hopper
HOPADD = $00 ' Hopper address number 0 - 7 for daisy chaining additional hoppers
obj
term : "fullduplexserialplus"
hopper: "fullduplexserialplus"
var
byte Ser_Num ' used to keep track of each dispense command so as not to repeat
byte ACK_Message[12] ' buffer for the ACK messages
word Num ' Number of coins
pub main | d
term.start(31, 30, %0000, 115_200) ' start terminal (use PST)
hopper.start(HOPRX, HOPTX, %0000, 9600) ' start hopper serial port
pause(600)
term.str(@ver)
pause(1000)
Reset_Dispenser
pause(1000)
'Ser_Num := 1 ' set serial number
Num := 1
Dispense
pause(1000)
Num := 4
Dispense
pub Dispense | chk1, chk2, d, idx
idx := 0
hopper.rxflush
hopper.tx($ED) ' Header
hopper.tx($08) ' Number of bytes in message. Always 8
hopper.tx(Ser_Num) ' Command serial number. Should increment after each successful command
hopper.tx($50) ' Dispense command
hopper.tx(HOPADD)
hopper.tx(Num & $FF) ' Lower byte of quantity to dispense
hopper.tx(Num >> 8) ' Higher byte of quantity to dispense
chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ HOPADD ^ (Num & $FF) ^ (Num >> 8)
hopper.tx(chk1)
hopper.rxflush
d := hopper.rxtime(200)
if d == $FD
byte[ACK_Message][idx] := d
chk1 := d
idx++
repeat 4
d := hopper.rx
byte[ACK_Message][idx] := d
chk1 := chk1 ^ d
idx++
chk2 := hopper.rx
byte[ACK_Message][idx] := chk2
if chk1 == chk2
Ser_Num++
repeat idx from 0 to 5
term.hex(byte[ACK_Message][idx], 2)
term.tx(32)
term.str(string("Hopper Dispense ACK OK", 13))
else
term.str(string("Bad Checksum for Hopper Dispense...", 13))
else
term.hex(d, 2)
term.tx(32)
term.str(string("No comms for Hopper Dispense...", 13))
pub Reset_Dispenser | chk1, chk2, d, idx
hopper.rxflush
hopper.tx($ED) ' Header
hopper.tx($08) ' Number of bytes in message. Always 8
hopper.tx($00) ' Command serial number. Sending a "0" will reset the counter in hopper
hopper.tx($50) ' Dispense command
hopper.tx(HOPADD)
hopper.tx($00) ' Lower byte of quantity to dispense
hopper.tx($00) ' Higher byte of quantity to dispense
chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ HOPADD ^ (Num & $FF) ^ (Num >> 8)
hopper.tx(chk1)
idx := 0
chk1 := 0
chk2 := 0
d := hopper.rxtime(200)
if d == $FD
byte[ACK_Message][idx] := d
chk1 := d
idx++
repeat 4
d := hopper.rx
byte[ACK_Message][idx] := d
chk1 := chk1 ^ d
idx++
chk2 := hopper.rx
byte[ACK_Message][idx] := chk2
if chk1 == chk2
Ser_Num++
repeat idx from 0 to 5
term.hex(byte[ACK_Message][idx], 2)
term.tx(32)
term.str(string("Reset Hopper ACK OK", 13))
else
term.str(string("Bad Checksum for Hopper Reset...", 13))
else
term.str(string("No comms for Hopper Reset...", 13))
pub pause(ms) | t
'' Delays program for ms milliseconds
t := cnt
repeat ms
waitcnt(t += MS_001)
Any suggestions?


Comments
did you try some debugging by sending all the raw bytes to the terminal that come in from the device?
or did you try to send debug-info after each received byte?
best regards
Stefan
I even tried changing the pause time between dispenses to 5 seconds which is way more time than needed to wait for dispensing coins and no difference.
Yes. See the screen shot of PST. It shows the message buffer from the hopper. The last line shows an FF for some reason. That is what I'm trying to solve.
Yes.
Here are the screen shots from the Logic Analyzer. The first one shows the command from the Prop to reset the hopper and the hoppers immediate response:
The second one shows the command to dispense 1 coin and the immediate response back from hopper:
The third shows the command to dispense 4 coins then the immediate response back from the hopper:
And btw.. I am NOT sharing the same power supply for the hopper with the prop board. It is a separate supply.
PUB rxdump repeat while (result := hopper.rxcheck) <> -1 term.str(string("Dumping $") term.hex(result, 2) term.tx(13)Do they share a common ground? If not they need to unless you're using something like RS-485.
I also think you're dumping the RX buffer too frequently. Dump it once before you start to send data but the way the code reads now you dump it again after the send which could easily throw away the first character.
I definitely think you should be checking to see what's getting dumped from the RX buffer as Dave suggested.
Edit: It looks like the two devices are communicating elsewhere in the code so I bet you're already using a common ground.
Here's my latest working code. I made quite a few changes from the original.
con _clkmode = xtal1 + pll16x ' Feedback and PLL multiplier _xinfreq = 5_000_000 ' External oscillator = 5 MHz MS_001 = 80_000_000 / 1_000 ' ticks in 1ms HOPRX = 0 ' to pin 6 on Hopper HOPTX = 1 ' to pin 5 on Hopper HOPADD = $00 ' Hopper address number 0 - 7 for daisy chaining additional hoppers obj term : "FullDuplexSerial256" hopper: "FullDuplexSerial256" var byte Ser_Num ' used to keep track of each dispense command so as not to repeat byte ACK_Message[12] ' buffer for the ACK messages word Num ' Number of coins pub main | d, idx term.start(31, 30, %0000, 115_200) ' start terminal (use PST) hopper.start(HOPRX, HOPTX, %0000, 9600) ' start hopper serial port pause(600) term.str(@ver) hopper.rxflush Ser_Num := 0 pause(1000) Dispense(0, 1, HOPADD) ' Reset hopper pause(500) Num := 1 Dispense(Num, 0, HOPADD) ' Dispense 1 coin Inquire pause(2000) Dispense(12, 0, HOPADD) ' Dispense 12 coins Inquire pub Dispense(Coins, Reset, Addr) | chk1, chk2, d, idx if Reset == 1 term.str(string("Sending Hopper Reset Command ")) else term.str(string("Sending Hopper Dispense Command ")) idx := 0 hopper.rxflush hopper.tx($ED) ' Header hopper.tx($08) ' Number of bytes in message. Always 8 if Reset == 0 hopper.tx(Ser_Num) ' Command serial number. Should increment after each successful dispense elseif Reset == 1 hopper.tx($00) Ser_Num := 0 hopper.tx($50) ' Dispense command hopper.tx(addr) hopper.tx(Coins & $FF) ' Lower byte of quantity to dispense hopper.tx(Coins >> 8) ' Higher byte of quantity to dispense chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ Addr ^ (Coins & $FF) ^ (Coins >> 8) hopper.tx(chk1) if Reset == 1 Ser_Num := 1 d := hopper.rxtime(200) if d == $FD term.hex(d, 2) term.tx(32) chk1 := d idx++ repeat 4 d := hopper.rx chk1 := chk1 ^ d term.hex(d, 2) term.tx(32) idx++ chk2 := hopper.rx term.hex(chk2, 2) term.tx(32) if chk1 == chk2 if Reset == 0 term.str(string(" Hopper Dispense ACK OK", 13)) else term.str(string(" Hopper Reset ACK OK", 13)) else term.str(string(" Bad Checksum for Hopper Dispense...", 13)) else term.str(string(" No comms for Hopper Dispense...", 13)) pub Inquire | chk1, chk2, d, idx term.str(string("Sending Hopper Inquire Command ")) hopper.tx($ED) ' Header hopper.tx($08) ' Number of bytes in message. Always 8 hopper.tx(Ser_Num) ' Command serial number. hopper.tx($51) ' Inquire command hopper.tx(HOPADD) hopper.tx($00) ' Lower byte of quantity to dispense hopper.tx($00) ' Higher byte of quantity to dispense chk1 := $ED ^ $08 ^ Ser_Num ^ $50 ^ HOPADD ^ (Num & $FF) ^ (Num >> 8) hopper.tx(chk1) idx := 0 chk1 := 0 chk2 := 0 d := hopper.rxtime(500) if d == $FD chk1 := d term.hex(d, 2) term.tx(32) idx++ repeat 6 d := hopper.rx chk1 := chk1 ^ d term.hex(d, 2) term.tx(32) idx++ chk2 := hopper.rx term.hex(chk2, 2) term.tx(32) if chk1 == chk2 Ser_Num++ term.str(string("Inquire Hopper ACK OK", 13)) else term.str(string("Bad Checksum for Hopper Inquire...", 13)) else term.str(string("No comms for Hopper Inquire...", 13)) Inquire pub pause(ms) | t '' Delays program for ms milliseconds t := cnt repeat ms waitcnt(t += MS_001)What's odd is that in the spec sheet it shows an INQUIRE command and one of the flags shows whether the hopper is busy or not. But the hopper won't reply at all while it's busy dispensing so I just continue to poll it till it responds. I'll need to add some sort of time out so that it doesn't hang there forever if there is a problem.
Here's a screen shot of PST showing it working:
Yes. Common ground.
???? Did you look at my latest code post? I flush the rx buffer once during initialization and only 1 other time before the dispense command is sent. You think that is too often? I figured I'd flush it right before the command was sent so the buffer is clean and waiting for the reply.
Anyway that seems to work fine. As I mentioned earlier I need to study the proper way to store the data received into an indexed buffer so that I may test certain bytes/bits for flags.
This is a Chinese made hopper and the command responses documented aren't the same that is experienced. Anyway I'll mark this as solved for now. Please continue to add any comments as you wish.
Thanks for everyone's help. :-)
Probably not. The code I saw before commenting had a rxFlush after the command was sent (or so I remember).
I'm glad it's working. Sounds like a fun project.