Posts Tagged ‘urdaneta city university’

A line follower is just like a hello world in robotics. Many have been made and nowadays are based on arduino boards, this time I want to go old school and build this simple line follower from parts on my bin. The DS89C430 is a fast 8051 core variant from Maxim, before the era of arduino these are the microcontrollers that only requires UART connection and do not require a external programmer to load the bootloader because it is loaded with bootloader already from the factory and only a terminal program is required to load the hex file generated from the assembler or compiler.

This line follower consist of 3 TCRT5000 reflective IR sensors that goest to a LM339 quad comparator that gives the microcontroller a high or low signal when a black line is detected or not. A L9110S motor driver module was used to drive the two DC motors in differential drive mode. The whole project is powered by a single 3.7V 18650 battery from an old laptop battery pack. The output is fed to a boost converter based on the classic MC34063 to boost the battery voltage to 5V. The complete schematic is show below.

follower

Schematic Diagram

The whole project is built on a strip board with mixed thru hole and surface mount components depending on what I have on my bin.

P_20161231_111255.jpg

P_20161231_111512.jpg

And now the code! Basically a logic based on its position on the track. One motor is stopped to make a turn. The code is compiled with SDCC.

code.PNG

Voila! a nice line follower that can navigate even a complicated track! See the video demo below

Advertisements

This is my first post for the year, been busy for a while. The Pbot is an entry level mobile robot learning platform locally available in e-gizmo here in the Philippines. It contains three channel IR collision sensor, 3 Channel line sensor and a dual motor driver. Any arduino compatible platform can be used as the controller. It is a fun mobot platform to play with, can be configured as a line follower, a mazer or a sumobot or even a soccer bot.
Pbot

A quick weekend project to play with this mobot platform is to control it with VB.net thru Bluetooth Serial Port. When playing with these toys, it is important to know whether your battery still have the juice so you can enjoy playing, but the old board (that I have) don’t have a connection for battery feedback. My quick solution is to put a voltage divider (two 100K resistors) between the battery terminals and feed it to the Analog Input 0 of the controller.

Remote

The Arduino program transmits data every second to the computer then waits for a character from the Computer, When a correct character is received, then it tells the motors what to do.

Voila! Vb.net Controlled Pbot with Battery Status Feedback

The code listing for the Arduino

/*
'Pbot Remote Control with VB.net 2010
'Copyright (C) 2014 Richard Myrick T. Arellaga
'
'This program is free software: you can redistribute it and/or modify
'it under the terms of the GNU General Public License as published by
'the Free Software Foundation, either version 3 of the License, or
'(at your option) any later version.
'
'This program is distributed in the hope that it will be useful,
'but WITHOUT ANY WARRANTY; without even the implied warranty of
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
'GNU General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see .
*/

int batt = 0;
float voltage = 0;

long prevMillis = 0;
long interval = 1000;
int rx = 0;

const int M1DIR = 8;
const int M1RUN = 9;
const int M2RUN = 10;
const int M2DIR = 11;

void setup()
{
pinMode(M1DIR,OUTPUT);
pinMode(M1RUN,OUTPUT);
pinMode(M2RUN,OUTPUT);
pinMode(M2DIR,OUTPUT);

Serial.begin(9600);

}

void loop()
{
//Run RTC and send battery update every second
unsigned long currentMillis = millis();
if(currentMillis - prevMillis > interval)
{

prevMillis = currentMillis;
batt = analogRead(0);
voltage = (batt/1023.0)* (2 * readVcc());
Serial.println(voltage);

}

//check serial port character receive
if(Serial.available()){
rx=Serial.read();

if(rx == 'W')
{
digitalWrite(M1DIR,HIGH);
analogWrite(M1RUN,250);
analogWrite(M2RUN,250);
digitalWrite(M2DIR,HIGH);
rx = 0;
}else if(rx == 'Q')
{
digitalWrite(M1DIR,LOW);
analogWrite(M1RUN,0);
analogWrite(M2RUN,0);
digitalWrite(M2DIR,LOW);
rx = 0;
}else if(rx == 'S')
{
digitalWrite(M1DIR,LOW);
analogWrite(M1RUN,250);
analogWrite(M2RUN,250);
digitalWrite(M2DIR,LOW);
rx = 0;
} else if(rx == 'D')
{
digitalWrite(M1DIR,HIGH);
analogWrite(M1RUN,250);
analogWrite(M2RUN,0);
digitalWrite(M2DIR,LOW);
rx = 0;
}
else if(rx == 'A')
{
digitalWrite(M1DIR,LOW);
analogWrite(M1RUN,0);
analogWrite(M2RUN,250);
digitalWrite(M2DIR,HIGH);
rx = 0;
}
}
}

//read actual VCC of arduino for a more accurate adc reading
long readVcc() {
long result;
// Read 1.1V reference against AVcc
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = 1126400L / result; // Back-calculate AVcc in mV

return result/1000;
}

VB.net Code Listing


'Pbot Remote Control with VB.net 2010
'Copyright (C) 2014 Richard Myrick T. Arellaga
'
'This program is free software: you can redistribute it and/or modify
'it under the terms of the GNU General Public License as published by
'the Free Software Foundation, either version 3 of the License, or
'(at your option) any later version.
'
'This program is distributed in the hope that it will be useful,
'but WITHOUT ANY WARRANTY; without even the implied warranty of
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
'GNU General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see .

Imports System.IO.Ports
Imports System.Text

Public Class Form1

Private pendingMsg As New StringBuilder()

Private Sub GetCOMPortList()
Dim i As Integer
Dim foundDifference = False

If cboCOMPorts.Items.Count = SerialPort.GetPortNames().Length Then
For Each s As String In SerialPort.GetPortNames()
If cboCOMPorts.Items(System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)).Equals(s) = False Then
foundDifference = True
End If
Next
Else
foundDifference = True
End If

If foundDifference = False Then
Return
End If

cboCOMPorts.Items.Clear()

For Each s As String In SerialPort.GetPortNames()
cboCOMPorts.Items.Add(s)
Next
cboCOMPorts.SelectedIndex = 0

End Sub

Private Sub serialPort1_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

Dim completeMsg As String
completeMsg = String.Empty

pendingMsg.Append(SerialPort1.ReadExisting())

If pendingMsg.Length >= 6 Then
completeMsg = pendingMsg.ToString(0, 6)
pendingMsg.Remove(0, 6)
Me.Invoke(New Action(
Sub()
txtRxData.Text = completeMsg
End Sub))
End If

End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
btnDisconnect.Enabled = False
GetCOMPortList()
End Sub

Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click

Try
SerialPort1.PortName = cboCOMPorts.Items(cboCOMPorts.SelectedIndex).ToString()
SerialPort1.BaudRate = 9600
SerialPort1.ReadTimeout = 1000
SerialPort1.Open()
btnDisconnect.Enabled = True
btnConnect.Enabled = False
Catch ex As Exception
btnDisconnect.PerformClick()
End Try
End Sub

Private Sub btnDisconnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDisconnect.Click
Try
SerialPort1.DiscardInBuffer()
SerialPort1.DiscardOutBuffer()
SerialPort1.Close()
btnConnect.Enabled = True
btnDisconnect.Enabled = False
Catch ex As Exception

End Try
End Sub

Private Sub btnFW_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFW.Click
If (SerialPort1.IsOpen()) Then
SerialPort1.Write("W")

End If
End Sub

Private Sub btnBack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBack.Click
If (SerialPort1.IsOpen()) Then
SerialPort1.Write("S")

End If
End Sub

Private Sub btnLeft_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLeft.Click
If (SerialPort1.IsOpen()) Then
SerialPort1.Write("A")

End If
End Sub

Private Sub btnRight_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRight.Click
If (SerialPort1.IsOpen()) Then
SerialPort1.Write("D")

End If
End Sub

Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
If (SerialPort1.IsOpen()) Then
SerialPort1.Write("Q")

End If
End Sub
End Class

LED signs are everywhere and they can be bought dirt chip nowadays, but the feeling is good if you can build your own. This project is based on Atmega328P with Arduino UNO bootloader and some shift registers and a driver. The schematic shown is the complete circuit of the project. You notice that there are even no current limiting resistors, the LED matrix is directly driven by the shift register. This circuit is just for testing and is not guaranteed for a long period of operation. The message buffer can accommodate up to 100 characters.

The demo video is a little garbled because I am only using a mobile phone , but the actual device the message is clear.

8x32Matrix

Schematic Diagram

A simple message uploader written in C# can be download from here. The uploader requires .NET Framework 4.0 Installed.

Message Uploader

Message Uploader

Arduino Sketch


#include "cmap.h"

int d;
unsigned char char_pointer;
char Display_Ram[38];
unsigned char mask;
char LED_DATA[100]= "8x32 LED Scrolling Message Display by tiktak 2013 ~";
char LED_BUFFER[100];
char count;
unsigned char shift_counter;
int rxctr = 0;

#define INTEN 6500

const char DAT = 8;
const char CLK = 10;
const char LAT = 9;

const char DAT1 = 11;
const char CLK1 = 13;
const char LAT1 = 12;
int i = 0;
void setup() {
 pinMode(CLK, OUTPUT);
 pinMode(DAT, OUTPUT);
 pinMode(CLK1, OUTPUT);
 pinMode(DAT1, OUTPUT);
 pinMode(LAT, OUTPUT);
 pinMode(LAT1, OUTPUT);

 Serial.begin(9600);

}

void loop(){
 if(Serial.available()){
 int rx = Serial.read();
 LED_BUFFER[rxctr] = rx;
 rxctr++;
 if(rx == '~'){
 for(int h=0; h<100; h++){
 LED_DATA[h] = 0;
 }
 rxctr = 0;
 for(int x = 0; x<100; x++){
 LED_DATA[x] = LED_BUFFER[x];
 }
 Clear_Display_Ram();
 }
 }

 Display();
 shift();
}

void scanRow(unsigned char row)
{
 int i;
 for(i = 0; i<8; i++)
 {
 if(row & 0x80){
 digitalWrite(DAT1,HIGH);
 }
 else{
 digitalWrite(DAT1,LOW);
 }

 digitalWrite(CLK1,HIGH);
 digitalWrite(CLK1,LOW);

 row <<= 1;
 }
 digitalWrite(LAT1,HIGH);
 digitalWrite(LAT1,LOW);

}

void shift(void)
{
 int x;
 for(x=0; x<38; x++){
 Display_Ram[x] = Display_Ram[x+1];
 }

shift_counter++;
 if(shift_counter == 6){
 shift_counter = 0;
 load();
 }

}

void Display(void)
{
 mask = 0x01;
 scan();
 scanRow(0x80);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);

mask = 0x02;
 scan();
 scanRow(0x40);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);

mask = 0x04;
 scan();
 scanRow(0x20);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);

mask = 0x08;
 scan();
 scanRow(0x10);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);

mask = 0x10;
 scan();
 scanRow(0x08);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);

mask = 0x20;
 scan();
 scanRow(0x04);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);

mask = 0x40;
 scan();
 scanRow(0x02);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);

mask = 0x80;
 scan();
 scanRow(0x01);
 for(d=0;d<INTEN;d++){}
 scanRow(0x00);
}

void load(void){
 char counter1;
 if(LED_DATA[char_pointer+1] == '~')
 char_pointer = 0;
 for(counter1 = 0; counter1 < 5; counter1++){
 Display_Ram[32 + counter1] = character_data[(LED_DATA[char_pointer] - 0x20)][counter1];
 }
 Display_Ram[37] = 00;
 char_pointer++;
}

void Clear_Display_Ram(void){
 unsigned char rcount;
 for(rcount = 0; rcount < 37; rcount++)
 Display_Ram[rcount] = 0x00;
}

void scan(void){
 for (count=0;count<32;count++){
 if ((Display_Ram[count] & mask) == mask)
 digitalWrite(DAT,HIGH);
 else
 digitalWrite(DAT,LOW);
 digitalWrite(CLK,HIGH);
 digitalWrite(CLK,LOW);
 }
 digitalWrite(LAT,HIGH);
 digitalWrite(LAT,LOW);
}

cmap.h


const unsigned char character_data[][6] =
{
 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //20-
 { 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00 }, //21-!
 { 0x00, 0x00, 0xE0, 0x00, 0xE0, 0x00 }, //22-"
 { 0x28, 0xFE, 0x28, 0xFE, 0x28, 0x00 }, //23-#
 { 0x24, 0x54, 0x7E, 0x54, 0x48, 0x00 }, //24-$
 { 0xC4, 0xC4, 0x10, 0x26, 0x46, 0x00 }, //25-%
 { 0x6C, 0x92, 0xAA, 0x44, 0x1A, 0x00 }, //26-&
 { 0x00, 0x00, 0xA0, 0xC0, 0x00, 0x00 }, //27-'
 { 0x00, 0x00, 0x38, 0x44, 0x82, 0x00 }, //28-(
 { 0x00, 0x82, 0x44, 0x38, 0x00, 0x00 }, //29-)
 { 0x28, 0x10, 0x6C, 0x10, 0x28, 0x00 }, //2A-*
 { 0x10, 0x10, 0x6C, 0x10, 0x10, 0x00 }, //2B-+
 { 0x00, 0x00, 0x0A, 0x0C, 0x00, 0x00 }, //2C-,
 { 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 }, //2D--
 { 0x00, 0x00, 0x06, 0x06, 0x00, 0x00 }, //2E-.
 { 0x04, 0x08, 0x10, 0x20, 0x40, 0x00 }, //2F-/
 { 0x7C, 0x82, 0x82, 0x82, 0x7C, 0x00 }, //30
 { 0x00, 0x42, 0xFE, 0x02, 0x00, 0x00 }, //31
 { 0x42, 0x86, 0x8A, 0x92, 0x62, 0x00 }, //32
 { 0x84, 0x82, 0xA2, 0xD2, 0x8C, 0x00 }, //33
 { 0x18, 0x28, 0x48, 0xFE, 0x08, 0x00 }, //34
 { 0xE4, 0xA2, 0xA2, 0xA2, 0x9C, 0x00 }, //35
 { 0x3C, 0x52, 0x92, 0x92, 0x0C, 0x00 }, //36
 { 0x80, 0x8E, 0x90, 0xA0, 0xC0, 0x00 }, //37
 { 0x6C, 0x92, 0x92, 0x92, 0x6C, 0x00 }, //38
 { 0x60, 0x92, 0x92, 0x94, 0x78, 0x00 }, //39
 { 0x00, 0x00, 0x6C, 0x6C, 0x00, 0x00 }, //3A-:
 { 0x00, 0x00, 0x6A, 0x6C, 0x00, 0x00 }, //3B-//
 { 0x00, 0x10, 0x28, 0x44, 0x82, 0x00 }, //3C-<
 { 0x28, 0x28, 0x28, 0x28, 0x28, 0x00 }, //3D-=
 { 0x00, 0x82, 0x44, 0x28, 0x10, 0x00 }, //3E->
 { 0x40, 0x80, 0x8A, 0x90, 0x60, 0x00 }, //3F-?
 { 0x7C, 0x82, 0xBA, 0xAA, 0xBA, 0x70 }, //40-@
 { 0x7E, 0x88, 0x88, 0x88, 0x7E, 0x00 }, //41-A
 { 0xFE, 0x92, 0x92, 0x92, 0x6C, 0x00 }, //42-B
 { 0x7C, 0x82, 0x82, 0x82, 0x44, 0x00 }, //43-C
 { 0xFE, 0x82, 0x82, 0x44, 0x38, 0x00 }, //44-D
 { 0xFE, 0x92, 0x92, 0x92, 0x82, 0x00 }, //45-E
 { 0xFE, 0x90, 0x90, 0x90, 0x80, 0x00 }, //46-F
 { 0x7C, 0x82, 0x92, 0x92, 0x5E, 0x00 }, //47-G
 { 0xFE, 0x10, 0x10, 0x10, 0xFE, 0x00 }, //48-H
 { 0x00, 0x82, 0xFE, 0x82, 0x00, 0x00 }, //49-I
 { 0x04, 0x02, 0x82, 0xFC, 0x80, 0x00 }, //4A-J
 { 0xFE, 0x10, 0x28, 0x44, 0x82, 0x00 }, //4B-K
 { 0xFE, 0x02, 0x02, 0x02, 0x02, 0x00 }, //4C-L
 { 0xFE, 0x40, 0x30, 0x40, 0xFE, 0x00 }, //4D-M
 { 0xFE, 0x20, 0x10, 0x08, 0xFE, 0x00 }, //4E-N
 { 0x7C, 0x82, 0x82, 0x82, 0x7C, 0x00 }, //4F-O
 { 0xFE, 0x90, 0x90, 0x90, 0x60, 0x00 }, //50-P
 { 0x7C, 0x82, 0x8A, 0x84, 0x7A, 0x00 }, //51-Q
 { 0xFE, 0x90, 0x98, 0x94, 0x62, 0x00 }, //52-R
 { 0x62, 0x92, 0x92, 0x92, 0x8C, 0x00 }, //53-S
 { 0x80, 0x80, 0xFE, 0x80, 0x80, 0x00 }, //54-T
 { 0xFC, 0x02, 0x02, 0x02, 0xFC, 0x00 }, //55-U
 { 0xF8, 0x04, 0x02, 0x04, 0xF8, 0x00 }, //56-V
 { 0xFC, 0x02, 0x1C, 0x02, 0xFC, 0x00 }, //57-W
 { 0xC6, 0x28, 0x10, 0x28, 0xC6, 0x00 }, //58-X
 { 0xE0, 0x10, 0x0E, 0x10, 0xE0, 0x00 }, //59-Y
 { 0x86, 0x8A, 0x92, 0xA2, 0xC2, 0x00 }, //5A-Z
 { 0x00, 0x00, 0xFE, 0x82, 0x82, 0x00 }, //5B-[
 { 0x40, 0x20, 0x10, 0x08, 0x04, 0x00 }, //5C-\

{ 0x00, 0x82, 0x82, 0xFE, 0x00, 0x00 }, //5D-]
 { 0x20, 0x40, 0xFE, 0x40, 0x20, 0x00 }, //5E-^
 { 0x01, 0x01, 0x01, 0x01, 0x01, 0x00 }, //5F-_
 { 0x80, 0x40, 0x20, 0x00, 0x00, 0x00 }, //60-`
 { 0x04, 0x2A, 0x2A, 0x2A, 0x1E, 0x00 }, //61-a
 { 0xFE, 0x12, 0x22, 0x22, 0x1C, 0x00 }, //62-b
 { 0x1C, 0x22, 0x22, 0x22, 0x04, 0x00 }, //63-c
 { 0x1C, 0x22, 0x22, 0x12, 0xFE, 0x00 }, //64-d
 { 0x1C, 0x2A, 0x2A, 0x2A, 0x18, 0x00 }, //65-e
 { 0x08, 0x3F, 0x48, 0x40, 0x20, 0x00 }, //66-f
 { 0x18, 0x25, 0x25, 0x25, 0x3E, 0x00 }, //67-g
 { 0xFE, 0x10, 0x20, 0x20, 0x1E, 0x00 }, //68-h
 { 0x00, 0x22, 0xBE, 0x02, 0x00, 0x00 }, //69-i
 { 0x02, 0x01, 0x21, 0xBE, 0x00, 0x00 }, //6A-j
 { 0x00, 0xFE, 0x08, 0x14, 0x22, 0x00 }, //6B-k
 { 0x00, 0x82, 0xFE, 0x02, 0x00, 0x00 }, //6C-l
 { 0x3E, 0x20, 0x18, 0x20, 0x1E, 0x00 }, //6D-m
 { 0x3E, 0x10, 0x20, 0x20, 0x1E, 0x00 }, //6E-n
 { 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00 }, //6F-o
 { 0x3F, 0x24, 0x24, 0x24, 0x18, 0x00 }, //70-p
 { 0x18, 0x24, 0x24, 0x14, 0x3F, 0x00 }, //71-q
 { 0x3E, 0x10, 0x20, 0x20, 0x10, 0x00 }, //72-r
 { 0x12, 0x2A, 0x2A, 0x2A, 0x04, 0x00 }, //73-s
 { 0x20, 0xFC, 0x22, 0x02, 0x04, 0x00 }, //74-t
 { 0x3C, 0x02, 0x02, 0x04, 0x3E, 0x00 }, //75-u
 { 0x38, 0x04, 0x02, 0x04, 0x38, 0x00 }, //76-v
 { 0x3C, 0x02, 0x0C, 0x02, 0x3C, 0x00 }, //77-w
 { 0x22, 0x14, 0x08, 0x14, 0x22, 0x00 }, //78-x
 { 0x38, 0x05, 0x05, 0x05, 0x3E, 0x00 }, //79-y
 { 0x22, 0x26, 0x2A, 0x32, 0x22, 0x00 }, //7A-z
 { 0x00, 0x10, 0x6C, 0x82, 0x00, 0x00 }, //7B-{
 { 0x00 }, //7C-|-BLANK BAR
 { 0x00, 0x82, 0x6C, 0x10, 0x00, 0x00 }, //7D-}
 { 0x18, 0x24, 0x7E, 0x24, 0x08, 0x00 },
 { 0 } //7F-DO NOT USE

};