Step 6: ATMega168 MCU Serial Communication
Having achieved success with the Basic STAMP, it was time to switch over to the final platform, Pokey's Orangutan LV168 board with an ATmega168 microcontroller. The older orangutan-lib library from Pololu includes some routines for serial communications over the 168's PD0 and PD1 pins. (Incidentally, Pololu now provides the Pololu AVR C/C++ Library). Thanks to the LEDs on the level shifter, I was quickly able to diagnose an issue and establish communications between the PC and the Orangutan. First I got the 'tan to send data to the PC.
Then I set up character-based echo with the following C program (some details are omitted here):
The Orangutan LV168 says hello
#include "util.h"
#include
#include "uart.h"
#include
int main()
{
uint8_t c;
lcd_init();
uartInit(); // initialize UART (serial port)
uartSetBaudRate(9600); // set UART speed to 9600 baud
while (1) {
if (uartReceiveByte(&c)) {
uartSendByte(c);
}
//sleep for a bit
delay_ms(50);
} // for
return 0;
} // main
#include "uart.h"
#include
int main()
{
uint8_t c;
lcd_init();
uartInit(); // initialize UART (serial port)
uartSetBaudRate(9600); // set UART speed to 9600 baud
while (1) {
if (uartReceiveByte(&c)) {
uartSendByte(c);
}
//sleep for a bit
delay_ms(50);
} // for
return 0;
} // main
Line-at-a-time echo, like I did with the BS2, didn't work at first. The orangutan-lib uart code is interrupt driven, working in the background, while the BS2 command only completes after data is sent (or the timeout is reached). So, on the '168, you set up the receive first, specifying a buffer and a flag, then wait for the flag to get set, then you can look at the buffer. Only after that can you call the receive function again. Here's the code:
lcd_init();
uartInit(); // initialize UART (serial port)
uartSetBaudRate(9600); // set UART speed to 9600 baud
uartInit(); // initialize UART (serial port)
uartSetBaudRate(9600); // set UART speed to 9600 baud
uint8_t recvBuf[32];
uint16_t rxBytes=0;
uint8_t recvflag=0;
uint8_t flag=0;
uint8_t done='\r';
while (1) {
// setup to receive data
uartReceiveBuffer(recvBuf, 15, &rxBytes, done, &recvflag); // set up receive
while (!recvflag) {
//sleep for a bit
delay_ms(50);
} //while
if (recvflag) {
lcd_string("*");
// echo back what we got
uartSendBuffer(recvBuf, rxBytes, &flag);
}
} //while
uint16_t rxBytes=0;
uint8_t recvflag=0;
uint8_t flag=0;
uint8_t done='\r';
while (1) {
// setup to receive data
uartReceiveBuffer(recvBuf, 15, &rxBytes, done, &recvflag); // set up receive
while (!recvflag) {
//sleep for a bit
delay_ms(50);
} //while
if (recvflag) {
lcd_string("*");
// echo back what we got
uartSendBuffer(recvBuf, rxBytes, &flag);
}
} //while
Step 6: ATMega168 MCU BlueSMiRF Communication
It was now time to leap forward to establish communication between the Mac application and the Orangutan LV168 over bluetooth. One thing I haven't mentioned is that one has to get the TX and RX lines right and that this can be tricky depending on what type of components you're dealing with.
Some devices, like PCs and MCUs transmit on their TX line and receive on their RX line as you'd expect. Other equipment like modems, label their lines with respect to the computer talking to them. They receive data from a PC on the PC's TX line and send data back to the PC on its RX line.
In RS-232 speak, the modems are called Data Communications Equipment (DCE) while computers or terminals like the PC or MCU are called Data Terminal Equipment (DTE).
So if you have a DTE connected to a DCE, just connect TX to TX and RX to RX. If you are trying to get two DTEs to talk, connect TX to RX and RX to TX.
You've heard of a null modem cable? That's what it does, swaps the TX and RX lines (among other things)
The BlueSMiRF acts as a DCE and the AVR MCU acts as a DTE. So you just use a straight cable. (I later found out that my Sparkfun FTDI breakout board also acts like a DTE).
At any rate, I implemented code on the MCU to send the +++ and AT+BTSRV=1 commands prior to performing the line-by-line echo (note that I'm not verifying the receipt of the "OK" message sent by the modem; I'll do that later).
delay_ms(2000);
strcpy((char *) buf, "+++");
uartSendBuffer(buf, strlen((char *) buf), &flag);
delay_ms(2000);
// AT+BTSRV=1
strcpy((char *) buf, "AT+BTSRV=1\r\n");
uartSendBuffer(buf, strlen((char *) buf), &flag);
delay_ms(2000);
strcpy((char *) buf, "+++");
uartSendBuffer(buf, strlen((char *) buf), &flag);
delay_ms(2000);
// AT+BTSRV=1
strcpy((char *) buf, "AT+BTSRV=1\r\n");
uartSendBuffer(buf, strlen((char *) buf), &flag);
delay_ms(2000);
I connected the BlueSMiRF to the MCU fired everything up and was able to connect. The Mac application successfully sent data and displayed the echo sent back by the MCU. Right on! With the baby steps finally done, next up is controlling the robot from the Mac.
Epilogue
Pokey's DIY Serial PCB
I since put together a simple breakout board for serial communications with the LV168. The board accepts either the BlueSMiRF modem, or my Sparkfun FTDI 5V breakout board so I can go wired or wireless.
I tossed a couple of LEDs on the board to indicate data transmit and receive. Although my DIY "silkscreen" layer was severely cockeyed and though I goofed up the schematic and had to hack the board, I'm pretty happy with the results. This is my smallest PCB to date at only 1.00" x 0.75" ! :)