Have you seen the Dataport Lazarus component set from Serbod? •serial port (UART, COM-port, FTDI). OpenGPSX Lazarus Component for GPS Interface programming. Decode NMEA messages, Display GPS skyplot view, Display GPS signal strength, Display.
I’ve communicated between a PC and an Arduino via UDP in the past, but I have never tried to use the serial connection (USB) on the Arduino. So in this experiment I have setup an arduino with various sensors and send commands from a PC to read sensors.
Since Lazarus / Free Pascal is my language of choice on the PC, the PC side will be written in that language. There are lots of examples of using C, Python, Processing, etc. on the PC side. If that is what you are trying to do, go do some googling.
As far as hardware goes, on the arduino I have a slide switch, motion detector, and LED. I want to allow the PC to read the position of the slide switch. If the switch is on, then it reads the motion detector. If motion is detected, then the PC sends a command to turn on the LED. I also want to play a wave file. Since that is not overly easy on the Arduino, once the PC detects motion it will play a wave file itself.
To allow the PC to talk to the Arduino, I setup a simple protocol. The PC will send a single byte ‘command’ to the arduino:
1 – Is Switch On?
2 – Is motion detected?
3 – Turn LED on
4 – Turn LED off
5 – Alive Packet (sent by Arduino only)
2 – Is motion detected?
3 – Turn LED on
4 – Turn LED off
5 – Alive Packet (sent by Arduino only)
The arduino responds by echoing the command and if there is any additional information, it is appended after the command, so the PC would transmit a “1” to determine if the slide switch is on. If it is, the arduino will return “1T”.
I decided to ‘packetize’ these commands with a header that contains a check sum and the length of the packet. This allows me to verify if the data got transmitted correctly. Not that I really expect that to be a problem over this USB cable, but I want to do it as part of the exercise in case I ever need the ability.
A header is prefixed to the command. This is a very simple header:
The schematic for my hardware is as follows:
and here is the actual hardware:
I’ll go thru some of the highlights of the Free Pascal program on the PC side first. The full source code is too long to post directly, so a download is available below.
I wrote the program to run as a console program (e.g. character mode, not windowed). I probably wouldn’t do that for a production program, but it simplifies the source code so you can more easily look at the source to understand what it does.
I used the serial library called synaser. This was one of the libraries recommended on the Lazarus site: http://wiki.freepascal.org/Hardware_Access#Synaser
I haven’t made it that far yet, but I expect this library to work on linux as well. It appears it was designed to do that.
I will admit I had a heck of a time getting synaser to work for me. I spent several hours and was about to get a different library. I started with the waitingData and recvByte methods and simply could not get it to work (see Nov 2015 update at end). Bytes were being dropped. I attempted to use recvString and it had issues as well.
I wish I know exactly what I did to get things running. I suspect it was switching the waitingData method to the canRead method. But I don’t have a serial protocol analyzer and couldn’t see exactly what was happening on the line so I may have been doing something stupid and accidently fixed it. This library seems to be well used so I figure the problem was mine. I just am not sure what that problem was.
Since this is a test program I don’t spend a lot of trouble dealing with error conditions (I check but I just bomb the program). Also, I don’t tend to write object-oriented code and definitely not when I’m just messing around.
My coding is very ‘different’ from most. I am very old school and expect things to align. When they do I can very quickly find what I want when looking thru code. So there is A LOT of white space.
![Lazarus effect full movie Lazarus effect full movie](/uploads/1/2/6/2/126288530/952740134.jpg)
The outer block is quite simple: if the switch is on and we get motion, turn on the LED and play a sound:
In the isSwitchOn and isMotionDetected procedures, we send a simple command to the arduino via p2a_sendStr, then read the response from the arduino using p2a_getstr:
Then we act on the response:
The p2a_getStr procedure calls ‘private’ procedures to receive the packet from the serial port and then decode it (checking and removing the header):
The p2a_i_recvPacket procedure is the one I had the most trouble with, getting data consistently from the serial port. Changing this may cause problems.
Because I couldn’t get synaser’s recvString method to work, I simply watch for characters and append them to the packet as I build it. When I get a linefeed, I’m done. To prevent the program from hanging if LF is never received, I have a 5 second timeout in my own code that will cause the infinite while to be exited.
Also note that while I exit on LF, I need to eat the CR character that proceeds it.
Now that we have a packet it will need to be decoded. This is done by p2a_i_decodePacket. Decode will check the physical packet length, then the logical packet length, and finally get a checksum of the packet and verify the data didn’t change during transit:
The p2a_i_computeChecksum procedure calculates a very simple, quick and dirty, single byte checksum. Since the packet is being transmitted over a serial connection, I need to make sure I don’t generate something that is going to cause problems for the hardware. I’m sure this procedure would make a purist choke, but as my boss used to say, “it’s good enough for who it’s for”.
I compute the checksum by XOR’ing all bytes in the packet (except the checksum byte). If the first bit is set I force it to 0. Finally if the result is < 32, I set a bit to bring it back into the displayable part of the character set.
Those are the key points to the Free Pascal code on the PC side. All of the hardware is monitored/activated in the same manner. So let’s look at the arduino side.
There is honestly very little on the arduino side of note. If you are familiar with programming the arduino, there will be nothing here to surprise you.
The setup function assigns the various pins to the hardware devices.
Inside of the loop function, the motion detector is checked for motion. If motion is detected, a timer starts and keeps being advanced as long as there is motion. Once the motion stops long enough, the timer pops.
Commands are retrieved from the serial port and then executed in the loop function. This should be pretty self-explanatory:
Note that the functions I use in the arduino are named the same as those for the PC and do the same thing. So in the example above, “a2p_sendStr(“5″)” will send the PC back the string “5”.
And a2p_sendStr calls a2p_i_encodePacket and a2p_i_xmitpacket on the arduino just as on the PC. So if you figure out the code on either side; the other side works exactly the same.
recvPacket and xmitPacket read the serial line just as any other arduino app would do. There is nothing surprising there:
Honestly, encode, decode, and computeChecksum work exactly the same way as they did on the PC, I simply rewrote them in C for the arduino.
Once implemented, this experiment works perfect. I can communicate with the arduino from the PC and I have no issues with dropped characters. Response is not instantaneous at the PC – on avg it will take 1/2 a second for the PC to respond to movement since the loop timeout is 1s. But that is more than adequate for any kind of hardware monitoring I would expect to do from a PC.
All of the source can be found in this zip file:
Here is a video demoing the hardware:
Nov 2015 Update:
Started working on a new Pascal program requiring the use of the Synapse library. Again, I could not get recvbyte to work properly e.g.:
This would print 1 character in the packet and be done.
A little more experimenting and I discovered the recvByte method clears waitingDatathe waitingData count. So you might enter the while statement with 5 characters, but after you read the first character, the next iteration has waitingData set to 0 rather than 4.
To get around this problem, I need to know the characters waiting and read them all before calling waitingData again. Something like this:
I think this is the first implementation of a “byte available” type of function that gets cleared when you retrieve a single byte. Kind of stupid, but at least know I and now you know.
Advertisements