FreeBasic communication with Arduino logic error?
Re: FreeBasic communication with Arduino logic error?
You can change the port any time you like, but the Arduino must be connected to
the system (COM3 is default for Arduino, if not already used otherwise).
I assume that you're using Windows (on Linux it would be a TTY device).
"Control Panel\Hardware and Sound\Device Manager" then open "Ports (Com & Lpt)"
double click the "USB to Serial Port (COM3)" afterwards, in the "Port Setting - Tab",
click "Advanced" (here in the dropdown list, another port can be chosen).
the system (COM3 is default for Arduino, if not already used otherwise).
I assume that you're using Windows (on Linux it would be a TTY device).
"Control Panel\Hardware and Sound\Device Manager" then open "Ports (Com & Lpt)"
double click the "USB to Serial Port (COM3)" afterwards, in the "Port Setting - Tab",
click "Advanced" (here in the dropdown list, another port can be chosen).
Re: FreeBasic communication with Arduino logic error?
For windows, I'm pretty sure this is how it works: When you plug your device (arduino) in to your PC for the first time, windows will automatically load a driver and assign a serial port that is not currently in use.Dr_D wrote:So, I can only get coms to work, if I use COM3. Is there an explanation for this? At first, I thought it was actually referring to the specific USB port my Arduino is hooked up to as COM3. However, it doesn't matter which USB port I connect to. Why is that?
The arduino will communicate a vendor ID (VID), a product ID (PID), and a serial number (SN) to windows. The VID/PID will instruct windows on which driver to use, and the serial number is used to track information about the device.
Windows remembers the COM port assignment based on arduino VID/PID/SN by saving it in the registry. So it doesn't matter which USB port you plug it in to.
If you open Arduino IDE, and check Tools | Board Info, you should get something like
Code: Select all
BN: Arduino/Genuino Uno
VID: 0x2341
PID: 0x0001
SN: 5593534222226291C1A1
2) Then, look for the specific VID&PID/SN, to find where windows stores information about the device.
3) In my case, I have ./VID_2341&PID_0001/5593534222226291C1A1/. Then from there if I look under ./Device Parameters/PortName I see that it is set to COM7. Actually, if you find and change this value in the registry, then plug in the arduino, it will be assigned to a whatever COM port you choose here.
4) Also, you can also set the assigned com port through windows "Device Manager" by finding the USB device and looking at Properties / Advanced.
Arduino Serial Communication Demo #4
Arduino Serial Communication Demo #4
For demo #4, we go back to basics and try to provide a solution for the issue raised in the opening post.
1) we want to send commands from the host (pc) and have the device (arduino) act on them
2) we really don't want to wait for a response. If there is no command (or response) the arduino and host should just keep going
Here is the design I came up with; what I am calling "One Byte Serial Protocol":
There's quite a bit we can do with 8 bits of data. And actually, I'm going to reserve the high bit for future, so this first iteration of the design is just with 7 bits.
The key features of the 1-byte message to notice are:
1) 1 bit reserved
2) 3 bits for the command (or response)
3) 4 bits for an address
Now the address might refer to actual device hardware pins, or it might just refer to 16 different "things" which might be a hardware pin, or could also be a software controlled bit.
Here is an example of a simple command & response for an arduino input:
- Host writes &h50 to the serial port which means: request read bit, address 0
- Device receives &h50, reads a digital input and responds with:
- a) &h20, which means address 0 = low
- b) &h30, which means address 0 = high
- c) nothing, device ignored command
Here is an example of a simple command & response for an arduino output:
- Host writes &h74 to the serial port which means: request set bit, address 4
- Device receives &h74, write digital output high (or not), and responds with:
- a) &h24, which means address 4 = low (request denied)
- b) &h34, which means address 4 = high (request acknowledged)
- c) nothing, device ignored command
The main purpose for reserving the high bit, is that we probably want to expand on the protocol at some point to allow multi-byte messages, for example to transfer the value of analog values to/from the arduino. For now, we will keep it simple with just 16 addressable bits.
Now it is possible that either host or arduino could ignore commands, possibly on purpose without any response. For that we will need to maintain some timeout timers, but I'll save that for a later demo.
I'll post the code for the demo in the next post.
For demo #4, we go back to basics and try to provide a solution for the issue raised in the opening post.
1) we want to send commands from the host (pc) and have the device (arduino) act on them
2) we really don't want to wait for a response. If there is no command (or response) the arduino and host should just keep going
Here is the design I came up with; what I am calling "One Byte Serial Protocol":
Code: Select all
/'
One Byte Serial Protocol
GENERAL I/O
7 654 3210
- --- ---- -------------------------
0 100 mmmm general message (request)
0 000 mmmm general message (false)
0 001 mmmm general message (true)
mmmm = message number (0 to 15)
ADDRESSABLE BIT I/O
7 654 3210
- --- ---- -----------------
0 101 aaaa read bit request
0 110 aaaa clear bit request
0 111 aaaa set bit request
0 010 aaaa status = false
0 011 aaaa status = true
aaaa = address (0 to 15)
RESERVED
7 654 3210
- --- ---- --------
1 ddd dddd reserved
'/
The key features of the 1-byte message to notice are:
1) 1 bit reserved
2) 3 bits for the command (or response)
3) 4 bits for an address
Now the address might refer to actual device hardware pins, or it might just refer to 16 different "things" which might be a hardware pin, or could also be a software controlled bit.
Here is an example of a simple command & response for an arduino input:
- Host writes &h50 to the serial port which means: request read bit, address 0
- Device receives &h50, reads a digital input and responds with:
- a) &h20, which means address 0 = low
- b) &h30, which means address 0 = high
- c) nothing, device ignored command
Here is an example of a simple command & response for an arduino output:
- Host writes &h74 to the serial port which means: request set bit, address 4
- Device receives &h74, write digital output high (or not), and responds with:
- a) &h24, which means address 4 = low (request denied)
- b) &h34, which means address 4 = high (request acknowledged)
- c) nothing, device ignored command
The main purpose for reserving the high bit, is that we probably want to expand on the protocol at some point to allow multi-byte messages, for example to transfer the value of analog values to/from the arduino. For now, we will keep it simple with just 16 addressable bits.
Now it is possible that either host or arduino could ignore commands, possibly on purpose without any response. For that we will need to maintain some timeout timers, but I'll save that for a later demo.
I'll post the code for the demo in the next post.
Arduino Serial Communication Demo #4 (continued)
Arduino Serial Communication Demo #4 (continued)
FYI, read more description of demo #4 in the previous post
For this demo #4, we are using the following, hardware:
For my example, since the switches I am using (KEY0 to KEY3) can be open, I've opted to enable the pullup resistors on the board. This has the effect of inverting the KEY status. It doesn't really matter though, because we are going to correct it in our command/response loop.
First the Arduino sketch:
Obviously, your exact construction of the circuit may, differ. Use the constants to configure which keys & leds are connected to each pin.
And the demo host program in fbc:
I've tried to keep the listings a simple as possible. Even though there is an opportunity to factor some common code, I have left the repetition in the code in the hopes that it is easiest to see the pattern.
If anyone reading this has some experience with multi-threaded programs, you may start to see some parallels with serial communication. We have 2 separate threads (fbc & arduino) each doing their own thing. They do not share any data. However, all data is communicated and serialized though the serial port connection. The serial port also provides a kind of hardware mutex in that reading on one side will not interfere with writing on the other side. The only thing missing so far is some kind of conditional wait for synchronizing. However, ideally, we want both sides to just keep on running and never wait for the other side. This will be possible if each side of the serial connection only acts on each others responses.
Summary thought: our goal is to write this control scheme / design so that neither side of the serial port communication is ever waiting for the other side to finish. -- more on this in a later (maybe next) demo.
FYI, read more description of demo #4 in the previous post
For this demo #4, we are using the following, hardware:
Code: Select all
.---------.
KEY0 --->| |---> LED0
KEY1 --->| Arduino |---> LED1
KEY2 --->| |---> LED2
KEY3 --->| |---> LED3
'---------'
|
Serial
|
.---------.
KYBD --->| host |---> DISPLAY
'---------'
First the Arduino sketch:
Obviously, your exact construction of the circuit may, differ. Use the constants to configure which keys & leds are connected to each pin.
Code: Select all
/* note: set the led_?_pin and keypad_?_pin
* constants to match physical connections to
* the board
*/
#define keypad_0_pin 7 /* INPUT, button 0, ON/OFF */
#define keypad_1_pin 6 /* INPUT, button 1, ON/OFF */
#define keypad_2_pin 5 /* INPUT, button 2, ON/OFF */
#define keypad_3_pin 4 /* INPUT, button 3, ON/OFF */
#define led_0_pin 12 /* OUTPUT, LED 0, ON/OFF */
#define led_1_pin 11 /* OUTPUT, LED 1, ON/OFF */
#define led_2_pin 10 /* OUTPUT, LED 2, ON/OFF */
#define led_3_pin 9 /* OUTPUT, LED 3, ON/OFF */
void setup() {
Serial.begin( 9600 );
while( !Serial )
delay(1);
pinMode( keypad_0_pin, INPUT );
pinMode( keypad_1_pin, INPUT );
pinMode( keypad_2_pin, INPUT );
pinMode( keypad_3_pin, INPUT );
// the keypad is only switches, so turn
// on the internal pull-up resistors.
// Note: button is pressed when digitalRead() == LOW
digitalWrite( keypad_0_pin, HIGH );
digitalWrite( keypad_1_pin, HIGH );
digitalWrite( keypad_2_pin, HIGH );
digitalWrite( keypad_3_pin, HIGH );
pinMode( led_0_pin, OUTPUT );
pinMode( led_1_pin, OUTPUT );
pinMode( led_2_pin, OUTPUT );
pinMode( led_3_pin, OUTPUT );
// write the general message ACK code,
// just to let the host know we are online
Serial.write( 0x10 );
}
/*
* write a message to Serial to respond to
* adressable bit request
*/
void answer( uint8_t address, boolean status )
{
uint8_t msg = (address & 0xf);
if( status )
msg |= 0x30; // bit is true
else
msg |= 0x20; // bit is false
// send the answer
Serial.write( msg );
}
void loop() {
uint8_t msg ;
if( Serial.available() > 0 )
msg = Serial.read();
else
msg = -1;
uint8_t cmd = msg & 0xf0; // command is upper 4 bits
uint8_t adr = msg & 0x0f; // address is lower 4 bits
/* bit addresses (for the serial protocol )
* 0 = Keypad 0 (w/ pullup resistor, LOW=pressed)
* 1 = Keypad 1 (w/ pullup resistor, LOW=pressed)
* 2 = Keypad 2 (w/ pullup resistor, LOW=pressed)
* 3 = Keypad 3 (w/ pullup resistor, LOW=pressed)
* 4 = LED 0 (HIGH=ON)
* 5 = LED 1 (HIGH=ON)
* 6 = LED 2 (HIGH=ON)
* 7 = LED 3 (HIGH=ON)
*/
switch( cmd )
{
case 0x50: // request, read bit
{
switch( adr )
{
case 0:
answer( 0, digitalRead( keypad_0_pin )==LOW );
break;
case 1:
answer( 1, digitalRead( keypad_1_pin )==LOW );
break;
case 2:
answer( 2, digitalRead( keypad_2_pin )==LOW );
break;
case 3:
answer( 3, digitalRead( keypad_3_pin )==LOW );
break;
default:
break;
}
break;
}
case 0x60: // request, clear bit (to LOW)
{
switch( adr )
{
case 4:
digitalWrite( led_0_pin, LOW );
answer( 4, false );
break;
case 5:
digitalWrite( led_1_pin, LOW );
answer( 5, false );
break;
case 6:
digitalWrite( led_2_pin, LOW );
answer( 6, false );
break;
case 7:
digitalWrite( led_3_pin, LOW );
answer( 7, false );
break;
}
break;
}
case 0x70: // request, set bit (to HIGH)
{
switch( adr )
{
case 4:
digitalWrite( led_0_pin, HIGH );
answer( 4, true );
break;
case 5:
digitalWrite( led_1_pin, HIGH );
answer( 5, true );
break;
case 6:
digitalWrite( led_2_pin, HIGH );
answer( 6, true );
break;
case 7:
digitalWrite( led_3_pin, HIGH );
answer( 7, true );
break;
}
break;
}
default:
break;
}
}
Code: Select all
#ifdef __FB_LINUX__
const DEVICE_COM = "/dev/ttyACM0"
#else
const DEVICE_COM = "COM7"
#endif
const DEVICE_FILE_NO = 1
/'
One Byte Serial Protocol
GENERAL I/O
7 654 3210
- --- ---- -------------------------
0 100 mmmm general message (request)
0 000 mmmm general message (false)
0 001 mmmm general message (true)
mmmm = message number (0 to 15)
ADDRESSABLE BIT I/O
7 654 3210
- --- ---- -----------------
0 101 aaaa read bit request
0 110 aaaa clear bit request
0 111 aaaa set bit request
0 010 aaaa status = false
0 011 aaaa status = true
aaaa = address (0 to 15)
RESERVED
7 654 3210
- --- ---- --------
1 ddd dddd reserved
'/
function msgToString( byval msg as ubyte ) as string
'' command is upper 4 bits
dim cmd as ubyte = msg and &hf0
'' address is lower 4 bits
dim adr as ubyte = msg and &h0f
select case cmd
case &h00: function = "general NAK"
case &h10: function = "general ACK"
case &h20: function = "bit " & adr & " is false"
case &h30: function = "bit " & adr & " is true"
case &h40: function = "general msg #" & adr
case &h50: function = "read bit " & adr
case &h60: function = "clear bit " & adr
case &h70: function = "set bit " & adr
case else
function = "reserved"
end select
end function
'' show request on screen and send to device
sub request( byval msg as ubyte )
'' show a human readable string for the command
color 10
print "command = " & hex(msg,2) & ", " & msgToString( msg )
'' write to the device
put #DEVICE_FILE_NO,,msg
end sub
'' show response on screen
sub response( byval msg as ubyte )
'' show a human readable string for the command
color 13
print "response = " & hex(msg,2) & ", " & msgToString( msg )
end sub
'' ----------------
'' MAIN
'' ----------------
if( open com(DEVICE_COM & ":9600,n,8,1,cs0,ds0,cd0,rs" As #DEVICE_FILE_NO) <> 0 ) then
print "error connecting to arduino"
end 1
end if
print "press F1 for help, ESCAPE to exit"
do
dim k as string = inkey
select case k
case chr(27) '' escape
exit do
case chr(&hff, &h3b) '' F1 - help
color 7
print "Commands:"
print
print "ESC Exit program"
print "0, 1, 2, 3 Read bit 0, 1, 2, 3 (buttons)"
print "a, b, c, d Set bit 4, 5, 6, 7 (LEDs)"
print "A, B, C, D Clear bit 4, 5, 6, 7 (LEDs)"
print
case "0": request( &h50 ) '' read bit 0
case "1": request( &h51 ) '' read bit 1
case "2": request( &h52 ) '' read bit 2
case "3": request( &h53 ) '' read bit 3
case "A": request( &h64 ) '' clear bit 4
case "B": request( &h65 ) '' clear bit 5
case "C": request( &h66 ) '' clear bit 6
case "D": request( &h67 ) '' clear bit 7
case "a": request( &h74 ) '' set bit 4
case "b": request( &h75 ) '' set bit 5
case "c": request( &h76 ) '' set bit 6
case "d": request( &h77 ) '' set bit 7
case is > ""
color 7
print "key = ";
for i as integer = 1 to len(k)
print hex(asc(k,i),2); " ";
next
print
end select
dim msg as ubyte = 0
while( loc(DEVICE_FILE_NO) > 0 )
get #DEVICE_FILE_NO,,msg
response( msg )
wend
sleep 25
loop
close #DEVICE_FILE_NO
color 7
If anyone reading this has some experience with multi-threaded programs, you may start to see some parallels with serial communication. We have 2 separate threads (fbc & arduino) each doing their own thing. They do not share any data. However, all data is communicated and serialized though the serial port connection. The serial port also provides a kind of hardware mutex in that reading on one side will not interfere with writing on the other side. The only thing missing so far is some kind of conditional wait for synchronizing. However, ideally, we want both sides to just keep on running and never wait for the other side. This will be possible if each side of the serial connection only acts on each others responses.
Summary thought: our goal is to write this control scheme / design so that neither side of the serial port communication is ever waiting for the other side to finish. -- more on this in a later (maybe next) demo.
Re: FreeBasic communication with Arduino logic error?
Hey guys, I have another question... more like a problem I'm unable to solve myself, so I'm sorry if this is a lengthy post.
I've wired up a binary clock led display using some SN74HC595N IC's as shift registers because this project requires more pins than the Arduino offers. To get to the meat of it, I'm having trouble reading the data that FB sends using CoderJeff's serial coms library. Like I said, it's a binary clock, so from FB, I'm just sending a string of 18 characters, where each character is either 1 or 0, and then I add the terminator character to the end of the string. I'm having difficulty translating this string into the data I need in Arduino to control the leds.
So, this is the 18 character string I'm sending from the FreeBASIC side...
On the Arduino side, I've tried several things, but nothing has worked so far. As a matter of fact, it seems like some of my tests have only returned garbage. How can I read the string on the Arduino side, bit by bit?
On a side note, I even tried adding this to your class MessageBuffer...
I've wired up a binary clock led display using some SN74HC595N IC's as shift registers because this project requires more pins than the Arduino offers. To get to the meat of it, I'm having trouble reading the data that FB sends using CoderJeff's serial coms library. Like I said, it's a binary clock, so from FB, I'm just sending a string of 18 characters, where each character is either 1 or 0, and then I add the terminator character to the end of the string. I'm having difficulty translating this string into the data I need in Arduino to control the leds.
So, this is the 18 character string I'm sending from the FreeBASIC side...
Code: Select all
sHour = bin(val(mid(aTime,1)), 6)
sMinute = bin(val(mid(aTime,4)), 6)
sSecond = bin(val(mid(aTime,7)), 6)
dim as string k = sHour+sMinute+sSecond+chr(13)
On a side note, I even tried adding this to your class MessageBuffer...
Code: Select all
public:
char myChar( int i )
{
return msg_buffer[i];
}
-
- Posts: 3906
- Joined: Jan 01, 2009 7:03
- Location: Australia
Re: FreeBasic communication with Arduino logic error?
A string is a list of characters. Read each character one at a time in a for loop.Dr_D wrote: How can I read the string on the Arduino side, bit by bit?
Are you using any online tutorial example?
Test your Arduino code using the Serial Monitor.
Type in your 18 long string in the edit box then click the Send button.
It would probably help if you posted the Arduino code.
In the main loop I imagine it reads the string and then loops through each character.
If the character is '1' then it pulses out a HIGH. If the character is a '0' then it pulses out LOW?
If each shift register is only to display 6 LEDS then you need send out 2 dummy LOWS as well.
Re: FreeBasic communication with Arduino logic error?
I'll post the arduino code when I get home... But, yeah, I'm iterating through the list, and I seem to keep getting garbage. I didn't know about serial monitor... Thanks!
Re: FreeBasic communication with Arduino logic error?
Here is my Arduino sketch...
Code: Select all
//This is the Arduino tutorial for daisy-chaining shift registers that I followed...
//https://www.marginallyclever.com/2017/02/daisy-chain-74hc595n-shift-registers/
#include "MessageBuffer.h"
static MessageBuffer msg;
const int SER = 12; // (595 pin 14)serial input line goes to the *first* shift register... daisy chain, so each QH(PIN 9) goes to the next SER, etc, etc...
const int SRCLK = 11; // (595 pin 11)the serial clock line, or the sync pulse, normal state is low... write, high, back to low, etc...
const int SRCLR = 10; // (595 pin 10)clear memory line, normal state is high... drop to low then back to high to clear memory...
const int RCLK = 9; // (595 pin 12)copy register line, normal state is low... send high then back to low to copy states...
const int OE = 8; // (595 pin 13)enable output line... send low to enable, high to disable...
#define TOTAL_SHIFT_PINS 18
void setup()
{
Serial.begin( 9600 );
while ( !Serial )
delay(1);
Serial.print( "Arduino is online - demo 3\n" );
pinMode(SER, OUTPUT);
pinMode(SRCLK, OUTPUT);
pinMode(SRCLR, OUTPUT);
pinMode(RCLK, OUTPUT);
pinMode(OE, OUTPUT);
SR_clearRegisters();
SR_copyToStorage();
SR_turnOutputsOn();
}
void loop()
{
int testText[18];
int cnt = 0;
while ( Serial.available() > 0 )
{
int16_t ch = Serial.read();
if (cnt < 18) testText[cnt] = (int)ch;
cnt += 1;
if ( msg.addChar( ch ) )
break;
}
if ( msg.isComplete() )
{
if ( strcmp( "hello", msg.text() ) == 0 )
{
Serial.print( "HELLO back at you!\n" );
}
else
{
//this is just a test case to make sure the IC/led logic is working properly
int inData[18]; //= {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
for (int i = 0; i < TOTAL_SHIFT_PINS; i++)
{
inData[i] = random(0,2);
}
SR_turnOutputsOff();
for (int i = 0; i < TOTAL_SHIFT_PINS; i++)
{
if (testText[i] == 0)
//switch this out to test the wiring with rtandom values...
//if (inData[i] == 0)
{
SR_shiftDataIn(LOW);
}
else
{
//ditto...
SR_shiftDataIn(HIGH);
}
SR_copyToStorage();
}
SR_turnOutputsOn();
}
// we are done with the command message now, so clear it.
msg.clear();
}
}
void SR_turnOutputsOn()
{
digitalWrite(OE, LOW);
}
void SR_turnOutputsOff()
{
digitalWrite(OE, HIGH);
}
void SR_clearRegisters()
{
digitalWrite(SRCLR, LOW);
digitalWrite(SRCLR, HIGH);
}
void SR_shiftDataIn(int data)
{
digitalWrite(SER, data);
digitalWrite(SRCLK, HIGH);
digitalWrite(SRCLK, LOW);
}
void SR_copyToStorage()
{
digitalWrite(RCLK, HIGH);
digitalWrite(RCLK, LOW);
}
Arduino Serial Communication Concepts
I will try to describe a couple of concepts that apply to both the Arduino sketch and the fbc program, that may help to get an understanding of what's going on (or why the results are not the expected results). And, in the next post, I will give a demo, hopefully that shows all this clearly.Dr_D wrote:On the Arduino side, I've tried several things, but nothing has worked so far. As a matter of fact, it seems like some of my tests have only returned garbage. How can I read the string on the Arduino side, bit by bit?
1) Know the message
The possibilities for a format / protocol for a message sent over a serial wire are countless. Some message formats are defined in standards. Here we are creating a custom message format.
The question that has to answered is, what is a valid message? Once that is defined, some of the other programming becomes easier, as we check for either a good message or a bad message. If it's a good message, use it. If it's a bad message, then recover; either ignore it and wait for the next message, or report an error back to the sender.
Dr_D, in your case the message is a binary time code:
- 18 bytes, either "1" or "0"
- followed by chr(13), aka CR, carriage return
- get anything else? it's a bad message
2) Serial communication is separate from program execution.
Compared to serial communication, the statements in loop() procedure execute fast. It's possible that some serial data has be received, but there might be more data on the way. And if the next byte isn't quite ready to be read, there is no more data immediately available, so execution continues.
So, there's no guarantee that the call to loop() or the serial statements are going to synchronize.
3) Non-blocking (no wait) serial communication
There are some kinds of communication where each end of the conversation waits for some amount of time (or forever) for the other side to communicate. Though I feel, if my memory serves me correctly, that this is usually controlled with some hardware hand-shaking. We are not using any hardware flow control, and we are not waiting on the other device to communicate or finish it's message. Because, if the two devices get out of sync, we can deadlock; just like threading where 2 threads are each waiting for the other, and therefore never continue.
For us, whenever we poll for serial input, we just read what's there and move on, even if we only get part of a message, or no message at all.
4) The Arduino loop() is a procedure (not actually a loop)
The loop() is called many times, over and over. It's just like a procedure, so local variables are destroyed and reset on next entry. The only way to preserve state between calls is to use static storage.
In our case, since we are non-blocking, possibly only receiving part messages at a time, we need to preserve the current state at the end of loop() so we can continue the next time loop() is entered.
I could go on a little more, but let's get the demo code posted...
Arduino Serial Communication Demo #5
Arduino Serial Communication Demo #5
For this demo, we don't need to build anything. We are using:
- Serial Port
- LED_BUILTIN for an output
We are also not using any classes. Hopefully the program listings show the sequence more than anything. This demo should feel like it goes with the concepts listed in the previous post.
fbc host program:
- note that we are using chr(13) for the "end of message" character, just to be different. In previous demos, I had been using chr(10) as the end of message character.
And the arduino sketch:
For this demo, we don't need to build anything. We are using:
- Serial Port
- LED_BUILTIN for an output
We are also not using any classes. Hopefully the program listings show the sequence more than anything. This demo should feel like it goes with the concepts listed in the previous post.
fbc host program:
- note that we are using chr(13) for the "end of message" character, just to be different. In previous demos, I had been using chr(10) as the end of message character.
Code: Select all
#ifdef __FB_LINUX__
const DEVICE_COM = "/dev/ttyACM0"
#else
const DEVICE_COM = "COM7"
#endif
const DEVICE_FILE_NO = 1
'' ----------------
'' MAIN
'' ----------------
if( open com(DEVICE_COM & ":9600,n,8,1,cs0,ds0,cd0,rs" for binary As #DEVICE_FILE_NO) <> 0 ) then
print "error connecting to arduino"
end 1
end if
print "press F1 for help, ESCAPE to exit"
do
dim k as string = inkey
select case k
case chr(27) '' escape
exit do
case chr(&hff, &h3b) '' F1 - help
color 7
print "Commands:"
print
print "ESC Exit program"
print "t show current time"
print "w write current time to arduino"
print
'' display current time? or write timecode to device?
case "t", "w"
dim as string aTime, sHour, sMinute, sSecond, sTimeCode
aTime = time
sHour = bin(val(mid(aTime,1,2)), 6)
sMinute = bin(val(mid(aTime,4,2)), 6)
sSecond = bin(val(mid(aTime,7,2)), 6)
sTimeCode = sHour+sMinute+sSecond+chr(13)
color 12
print "Time = "; aTime
if( k = "w" ) then
color 10
print "TimeCode = "; sTimeCode
put #DEVICE_FILE_NO,,sTimeCode
end if
end select
'' show whatever the device sends us...
while( loc(DEVICE_FILE_NO) > 0 )
dim b as ubyte
get #DEVICE_FILE_NO,,b
color 13
select case b
case 32 to 127
print chr(b);
case else
print "<"; hex(b,2); ">";
end select
'' CR or LF? don't care, just print a new line
if( b = 10 or b = 13 ) then
print
end if
wend
sleep 25, 1
loop
close #DEVICE_FILE_NO
color 7
Code: Select all
/*
* Time Code Message Format:
* hhhhhhmmmmmmssssss<CR>
*
* hhhhhh = hours in binary text format ('0' or '1' characters only)
* mmmmmm = minutes in binary text format ('0' or '1' characters only)
* ssssss = seconds in binary text format ('0' or '1' characters only)
* <CR> = end of message character chr(13)
*
* 19 bytes total
*/
void setup() {
// use the on-board LED to show something
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin( 9600 );
while( !Serial )
delay(1);
Serial.print( "Arduino is online - demo 5\n" );
}
void loop()
{
// preserve inTimeCode[] and inCount between calls,
// we may not receive the entire message in one loop through
static uint8_t inTimeCode[19];
static int inCount = 0;
int16_t ch = -1;
// read one char at a time (basically guarantees we never
// see the whole message in one call to loop()
ch = Serial.read();
// valid char? add it to inTimeCode[]
if( ch != -1 )
{
inTimeCode[inCount] = ch;
inCount += 1;
}
// buffer full, but no CR? it's bad, start over
if( (ch != 13) && (inCount == 19) )
{
inCount == 0;
Serial.write( "bad time code, too long" );
Serial.write( 13 );
}
// got a CR, but buffer not full yet? it's bad, start over
if( (ch == 13) && (inCount != 19) )
{
inCount == 0;
Serial.write( "bad time code, too short" );
Serial.write( 13 );
}
// got a CR and buffer full? maybe it's OK... let's check
if( (ch == 13) && (inCount == 19) )
{
for( int i = 0; i < 18; ++i )
{
if( (inTimeCode[i] != '0') && (inTimeCode[i] != '1') )
{
// bad data, start over
inCount == 0;
Serial.write( "bad time code, invalid data" );
Serial.write( 13 );
break;
}
}
// we already know last char is CR, so no need to check that
}
// still have a full buffer? it has to
// be good... we threw all the bad data away
if( inCount == 19 )
{
// toggle the status of LED_BUILTIN to show we got a valid time code
digitalWrite( LED_BUILTIN, !digitalRead( LED_BUILTIN ) );
// send it back to the host, one bit at a time for fun...
for( int i = 0; i < 18; ++i )
{
Serial.write( inTimeCode[i] );
Serial.write( 32 );
}
Serial.write( 13 );
// done with the valid time code, start over, so we can get the next one
inCount = 0;
}
}
Re: FreeBasic communication with Arduino logic error?
Thanks dude. This is awesome. I need to take some time to wrap my head around it. I still don't understand why it isn't over when if(serial) is over... but all I can do is learn what's going on. Thanks again. :)
EDIT:
At the time of this post, I haven't even read most of it...
EDIT:
At the time of this post, I haven't even read most of it...
Re: FreeBasic communication with Arduino logic error?
No problem.
Overall, we are only using a handful of commands for serial communication. Open COM, Loc(), Get#, Put#, etc. And the use of those commands is not so different from file I/O operations. Except for file I/O, the OS and drivers takes care of the timing, buffering, and flow control, with the hardware, and we hardly need to be concerned with it. With serial communication, the issues are much more exposed, especially when devices have limited resources or speeds of components are very different.
I think I have a fairly good understanding, but I may not explain it well, so I appreciate the comments and feedback. It kind of helps direct my next effort.
Overall, we are only using a handful of commands for serial communication. Open COM, Loc(), Get#, Put#, etc. And the use of those commands is not so different from file I/O operations. Except for file I/O, the OS and drivers takes care of the timing, buffering, and flow control, with the hardware, and we hardly need to be concerned with it. With serial communication, the issues are much more exposed, especially when devices have limited resources or speeds of components are very different.
I think I have a fairly good understanding, but I may not explain it well, so I appreciate the comments and feedback. It kind of helps direct my next effort.
Arduino Serial Communication Demo #6
Arduino Serial Communication Demo #6
This demo is a simulation of serial communication. We don't need any connected devices for this program to run.
It is intended to show what's happening in the serial communication as the message gets passed from program to serial port to device, etc.
Things to try:
- type on the keyboard to start messages
- try toggling buffering on/off for different components (messages will need an enter to be processed)
- try changing the speeds of the host, serial com-link, and device, to see what happens with message delivery
- try flooding the buffers with lots of key presses to overflow buffers and drop characters
Here is the listing. Sorry, the code is not well documented, as the main purpose for this program is the simulation.
A couple notes about the listing:
- the simulation is constructed using a singly linked list of components
- data moves from one component to the next based on timers and other properties
- the simulation "timing" is done by "number of frames" and there is a simple hard-coded sleep 25 for display timing
- TAB will pause simulation
This demo is a simulation of serial communication. We don't need any connected devices for this program to run.
It is intended to show what's happening in the serial communication as the message gets passed from program to serial port to device, etc.
Things to try:
- type on the keyboard to start messages
- try toggling buffering on/off for different components (messages will need an enter to be processed)
- try changing the speeds of the host, serial com-link, and device, to see what happens with message delivery
- try flooding the buffers with lots of key presses to overflow buffers and drop characters
Here is the listing. Sorry, the code is not well documented, as the main purpose for this program is the simulation.
Code: Select all
'' Serial Communication Simulation
''
'' displays graphical simulation of
'' host communicating with device
'' over serial
'' character size
const SX = 8, SY = 8
'' helper macro to clamp values
#macro clamp( arg, min, max )
if( arg < min ) then
arg = min
elseif( arg > max ) then
arg = max
end if
#endmacro
'' Timer class to help manage timers
'' when created or after reset()
'' counts number of ticks
'' status is true (signalled) when
'' current time > duration
type OnDelayTimer
static time0 as integer
declare static sub nexttick()
time1 as integer
duration as integer
declare constructor( byval ondelay as integer )
declare function status() as boolean
declare sub reset()
end type
dim shared OnDelayTimer.time0 as integer
constructor OnDelayTimer( byval in_duration as integer )
duration = in_duration
end constructor
function OnDelayTimer.status() as boolean
return (time0 >= time1+duration)
end function
sub OnDelayTimer.nexttick()
time0 += 1
end sub
sub OnDelayTimer.reset()
time1 = time0
end sub
enum TIMER_ENUM
TIMER_NONE = -1
TIMER_GLOBAL = 0
TIMER_HOST
TIMER_SERIAL
TIMER_DEVICE
TIMER_COUNT
end enum
dim timers( 0 to TIMER_COUNT-1 ) as OnDelayTimer = _
{ _
OnDelayTimer( 0 ), _
OnDelayTimer( 10 ), _
OnDelayTimer( 30 ), _
OnDelayTimer( 20 ) _
}
enum STYLE_ENUM
STYLE_BOX
STYLE_WIRE
end enum
'' component class to manage the on-screen components
type Component
title as string
row1 as integer
col1 as integer
rows as integer
cols as integer
text as string
scroll as boolean
maxRemoveChar as integer
timerId as TIMER_ENUM
style as STYLE_ENUM
eolChar as integer
borderColor as integer
declare constructor _
( _
byref in_title as string, _
byval in_row1 as integer, byval in_col1 as integer, _
byval in_rows as integer, byval in_cols as integer, _
byval in_color as integer, _
byval in_timerId as TIMER_ENUM = TIMER_NONE, _
byval in_style as STYLE_ENUM = STYLE_BOX _
)
declare sub add( byref k as const string )
declare function remove( byval count as integer = 0, byval eol as integer = 0 ) as string
declare sub render()
end type
constructor Component _
( _
byref in_title as string, _
byval in_row1 as integer, byval in_col1 as integer, _
byval in_rows as integer, byval in_cols as integer, _
byval in_color as integer, _
byval in_timerId as TIMER_ENUM = TIMER_NONE, _
byval in_style as STYLE_ENUM = STYLE_BOX _
)
title = in_title
row1 = in_row1
col1 = in_col1
rows = in_rows
cols = in_cols
borderColor = in_color
timerId = in_timerId
style = in_style
end constructor
sub Component.add( byref k as const string )
if( scroll ) then
text &= k
while( len(text) > rows * cols )
text = mid( text, cols+1 )
wend
else
text = left( text & k, rows * cols )
end if
end sub
function Component.remove( byval count as integer = 0, byval eol as integer = 0 ) as string
if( count <= 0 ) then
count = len(text)
end if
if( eol > 0 ) then
count = instr( text, chr(eol) )
end if
function = left( text, count )
text = mid( text, count + 1 )
end function
sub Component.render()
dim x1 as single = (col1-1) * SX
dim y1 as single = (row1-1) * SY
dim x2 as single = (col1+cols-1) * SX
dim y2 as single = (row1+rows-1) * SY
if( style = STYLE_WIRE ) then
line ( x1+SX\2, y1+SY\2 ) - ( x2, y2-SY\2 ), 8, , &hf0f0
'' text
for row as integer = 0 to rows-1
dim s as string = mid( text, row*cols+1, cols )
draw string ( (x1 + x2 + len(s)*SX)\2, y1+(row*SY) ), s, 15
next
else
'' title
dim s as string = title
if( eolChar <> 0 ) then
s &= " (need CR)"
end if
draw string( x1 - SX\2, y1-(2*SY) ), s, 7
'' fill
if( text > "" ) then
line ( x1-2, y1-2 ) - ( x2+2, y2+2 ), 1, bf
end if
'' border
line ( x1-3, y1-3 ) - ( x2+3, y2+3 ), borderColor, b
'' text
for row as integer = 0 to rows-1
draw string ( x1, y1+(row*SY) ), mid( text, row*cols+1, cols ), 15
next
end if
end sub
enum COMPONENT_ID
CMP_DISPLAY
CMP_HOST_IN
CMP_HOST_RX
CMP_WIRE1
CMP_DEV_TX
CMP_DEV_OUT
CMP_DEV_IN
CMP_DEV_RX
CMP_WIRE2
CMP_HOST_TX
CMP_HOST_OUT
CMP_KEYBOARD
COMPONENT_COUNT
end enum
'' ----
'' MAIN
'' ----
dim components( 0 to COMPONENT_COUNT-1 ) as Component = _
{ _
Component( "Display", 5, 5, 5, 16, 15, TIMER_HOST ), _
Component( "Host In", 15, 5, 1, 16, 14, TIMER_HOST ), _
Component( "Serial In", 20, 5, 1, 16, 8, TIMER_SERIAL ), _
Component( "Wire 1", 20, 21, 1, 38, 8, TIMER_SERIAL, STYLE_WIRE ), _
Component( "Serial Out", 20, 60, 1, 16, 8, TIMER_DEVICE ), _
Component( "Device Out", 25, 60, 1, 16, 14, TIMER_DEVICE ), _
Component( "Device In", 30, 60, 1, 16, 14, TIMER_DEVICE ), _
Component( "Serial In", 35, 60, 1, 16, 8, TIMER_SERIAL ), _
Component( "Wire 2", 35, 21, 1, 38, 8, TIMER_SERIAL, STYLE_WIRE ), _
Component( "Serial Out", 35, 5, 1, 16, 8, TIMER_HOST ), _
Component( "Host Out", 40, 5, 1, 16, 14, TIMER_HOST ), _
Component( "Keyboard", 45, 5, 1, 16, 15, TIMER_NONE ) _
}
components( CMP_DISPLAY ).scroll = true
components( CMP_HOST_TX ).maxRemoveChar = 1
components( CMP_DEV_TX ).maxRemoveChar = 1
components( CMP_WIRE1 ).maxRemoveChar = 1
components( CMP_WIRE2 ).maxRemoveChar = 1
dim page as integer = 0
screenres 640, 480, 8, 2
screenset 1-page, page
dim paused as boolean
do
if( not paused ) then
timers( TIMER_GLOBAL ).nexttick()
end if
dim k as string = inkey
'' input
select case k
case chr(27) '' escape
exit do
case chr(9)
paused = not paused
case chr(13) '' enter
components( CMP_KEYBOARD ).add( k )
case chr(&hff,&h3b) '' F1
timers( TIMER_HOST ).duration += 1
case chr(&hff,&h54) '' Shift+F1
timers( TIMER_HOST ).duration -= 1
case chr(&hff,&h3c) '' F2
timers( TIMER_SERIAL ).duration += 1
case chr(&hff,&h55) '' Shift+F2
timers( TIMER_SERIAL ).duration -= 1
case chr(&hff,&h3d) '' F3
timers( TIMER_DEVICE ).duration += 1
case chr(&hff,&h56) '' Shift+F3
timers( TIMER_DEVICE ).duration -= 1
case chr(&hff,&h3e) '' F4
components( CMP_DISPLAY ).remove()
case chr(&hff,&h3f) '' F5
components( CMP_HOST_OUT ).eolChar xor=13
case chr(&hff,&h40) '' F6
components( CMP_DEV_IN ).eolChar xor=13
case chr(&hff,&h41) '' F7
components( CMP_DEV_OUT ).eolChar xor=13
case chr(&hff,&h42) '' F8
components( CMP_HOST_IN ).eolChar xor=13
case chr(&hff,&h43) '' F9
for i as integer = 0 to COMPONENT_COUNT-1
components( i ).text = ""
components( i ).eolChar = 0
next
case chr(32) to chr(126)
components( CMP_KEYBOARD ).add( k )
end select
clamp( timers( TIMER_HOST ).duration, 0, 100 )
clamp( timers( TIMER_SERIAL ).duration, 0, 100 )
clamp( timers( TIMER_DEVICE ).duration, 0, 100 )
'' simulation - move the data through the pipe line
for i as integer = 0 to COMPONENT_COUNT-2
if( timers( components(i).timerId ).status() ) then
components(i).add( components(i+1).remove( components(i+1).maxRemoveChar, components(i+1).eolChar ) )
end if
next
'' reset all elapsed timers
for i as integer = 1 to TIMER_COUNT-1
if( timers(i).status() ) then
timers(i).reset()
end if
next
'' render
cls
for i as integer = 0 to COMPONENT_COUNT-1
components(i).render()
next
if( paused ) then
color 12: locate 27, 20: print "---<<< P A U S E D >>>---"
end if
locate 5, 30
color 15: print using "Host Speed : ###%"; 100 - timers( TIMER_HOST ).duration;
color 7: print " (F1/Shift+F1 to adjust)"
locate 7, 30
color 15: print using "Serial Speed: ###%"; 100 - timers( TIMER_SERIAL ).duration;
color 7: print " (F2/Shift+F2 to adjust)"
locate 9, 30
color 15: print using "Device Speed: ###%"; 100 - timers( TIMER_DEVICE ).duration;
color 7: print " (F3/Shift+F3 to adjust)"
color 10: locate 48, 5: print "Type keys to start"
color 7: locate 50, 5: print "ESC to EXIT"
color 7: locate 40, 30: print "F4 to clear display"
color 14
locate 42, 30: print "F5 to toggle 'Host Out' buffering"
locate 44, 30: print "F6 to toggle 'Device In' buffering"
locate 46, 30: print "F7 to toggle 'Device Out' buffering"
locate 48, 30: print "F8 to toggle 'Host In' buffering"
color 7
locate 50, 30: print "F9 to reset everything"
'' page flip
page = 1-page
screenset 1-page, page
sleep 25, 1
loop
- the simulation is constructed using a singly linked list of components
- data moves from one component to the next based on timers and other properties
- the simulation "timing" is done by "number of frames" and there is a simple hard-coded sleep 25 for display timing
- TAB will pause simulation
Re: FreeBasic communication with Arduino logic error?
/*
Well, I've come to a conclusion. I don't think a serial connection is suitable for a binary clock. I can now pass the data correctly, but unpredictability of the timing of when the buffer is actually full isn't reliable enough for something like a clock. However, I have learned a ton. I can make my Christmas lights program now, which doesn't rely on any certain time interval. Program the data, send it, then it doesn't matter when it gets there... You'll just have to wait. lol
I'd like to say thanks again, to everyone who has participated in this. I've never dabbled with serial communication before, and I have learned a lot. :)
*/
Edit:
Here's a picture of it... It does work, but as I said, the reliability of the serial connection is an issue.
Edit+1:
Ok... I can't explain it, but the whole system fails, once in a while. It recovers on the next send, but I can't explain why all the lights drop off for one frame. Like everything drops off... No lights at all... Then, it recovers and displays the correct time again. I can't figure it out because it's so unpredictable.
Well, I've come to a conclusion. I don't think a serial connection is suitable for a binary clock. I can now pass the data correctly, but unpredictability of the timing of when the buffer is actually full isn't reliable enough for something like a clock. However, I have learned a ton. I can make my Christmas lights program now, which doesn't rely on any certain time interval. Program the data, send it, then it doesn't matter when it gets there... You'll just have to wait. lol
I'd like to say thanks again, to everyone who has participated in this. I've never dabbled with serial communication before, and I have learned a lot. :)
*/
Edit:
Here's a picture of it... It does work, but as I said, the reliability of the serial connection is an issue.
Edit+1:
Ok... I can't explain it, but the whole system fails, once in a while. It recovers on the next send, but I can't explain why all the lights drop off for one frame. Like everything drops off... No lights at all... Then, it recovers and displays the correct time again. I can't figure it out because it's so unpredictable.
-
- Posts: 3906
- Joined: Jan 01, 2009 7:03
- Location: Australia
Re: FreeBasic communication with Arduino logic error?
Your image doesn't show up for me.
I had to hold the right button down over the icon to get the image url and paste that into google in order to view the image.
I use,
https://postimages.org/
I will have to order some of those SN74HC595N IC's to give it a go.
Are you going to post your code? Maybe someone will figure out the reason you get the once in a while system fail?
I had to hold the right button down over the icon to get the image url and paste that into google in order to view the image.
I use,
https://postimages.org/
I will have to order some of those SN74HC595N IC's to give it a go.
Are you going to post your code? Maybe someone will figure out the reason you get the once in a while system fail?