WriterBot part 2
In this project we will combine the 'Text Entry' program with the 'WriterBot v1' program to make a program that will allow the user to enter a text string that will then be written out by the robot.
This is very straightforward - it's just necessary to check the variable names to make sure that there is no 'clash' of variable names i.e. makes sure that there are no variables with the same names in the two programs that are being used for different purposes.
I also renamed the 'mytext' variable in the text entry program to 'mystring' for consistency with the WriterBot code, and I added a few lines (highlighted below) to link the programs together. Here is the finished code for WriterBot v2. As usual, you can copy and paste this into Small Basic and try it for yourself. You know how to select long blocks of text, right? Left-click just before the start of the block, then hold down Shift and left-click just after the end of the block.
'WriterBot v2 by Nigel Ward
'See the 'Going Further' section of EV3Basic.com
'*******************TEXT ENTRY CODE STARTS HERE******************
'Allows a string of up to 11 characters to be typed
'This program is compatible with brick mode
'(it can be compiled to the brick)
'Each character has dimensions 16x22 on screen
'Columns are numbered 1-11
mystring=""
LCD.Clear()
LCD.Text(1,0,3,1," <=Backspace >=Enter")
line[1]="0123456789."
line[2]="ABCDEFGHIJK"
line[3]="LMNOPQRSTUV"
line[4]="WXYZ - < >"
LCD.Text(1,0,16,2,line[1])
LCD.Text(1,0,38,2,line[2])
LCD.Text(1,0,60,2,line[3])
LCD.Text(1,0,82,2,line[4])
LCD.Rect(1,0,103,178,25) 'draw rect around typing area
col=6 'current column
row=2 'current row
x=81 'x coordinate of highlighted char
y=35 'y coordinate of highlighted char
LCD.InverseRect(x,y,16,22)
loop="True" 'loop control variable
While loop
Buttons.Wait()
Button=Buttons.GetClicks()
LCD.InverseRect(x,y,16,22) 'clear inverse
If Button="R" Then
col=1+Math.Remainder(col,11) 'allow jump from col 11 to col 1
ElseIf Button="L" Then
col=11-Math.Remainder(12-col,11) 'allow jump from col 1 to col 11
ElseIf Button="U" Then
row=4-Math.Remainder(5-row,4) 'allow jump from row 4 to row 1
ElseIf Button="D" Then
row=1+Math.Remainder(row,4) 'allow jump from row 1 to row 4
ElseIf Button="E" Then
If row=4 and col>6 Then
If col =9 then 'backspace
If mystring<>"" Then
'remove last character from mystring
mystring=Text.GetSubText(mystring,1,text.GetLength(mystring)-1)
Else 'nothing to delete
Speaker.Tone(100,1000,1000)
Endif
ElseIf col = 11 Then 'Enter
Loop="False" ' do not repeat loop
Else 'col=7,8 or 10, which is not valid in row 4
Speaker.Tone(100,1000,1000)
EndIf
Else 'middle button was pressed over a normal character
If text.GetLength(mystring)<>11 Then
mystring=mystring+Text.GetSubText(line[row],col,1)
Else 'already have the maximum 11 characters
Speaker.Tone(100,1000,1000)
EndIf
EndIf
EndIf
x=-15+col*16 'calculate x coordinate of character to be highlighted
y=-9+row*22 'calculate y coordinate of character to be highlighted
LCD.InverseRect(x,y,16,22)
LCD.text(1,1,107,2," ") 'clear text
LCD.text(1,1,107,2,mystring)
EndWhile
LCD.Clear()
LCD.text(1,0,0,2,"Your text:")
LCD.text(1,0,20,2,mystring)
LCD.text(1,0,40,2,"Press Enter")
LCD.text(1,0,60,2,"when the")
LCD.text(1,0,80,2,"robot is")
LCD.text(1,0,100,2,"ready...")
Buttons.Wait()
Program.Delay(2000)
'*******************ROBOT WRITER CODE STARTS HERE******************
ports="BC"
penport="A"
scale=10 'desired letter height in cm
s = scale * 0.0515 'adjust scale factor
tf=2 'turnfactor = motor turn angle / robot turn angle
'depends on wheel separation and wheel diameter
'recommended value =2 for standard
'driving base with wheel separation=12.5cm and wheel diam=5.6cm
sf = 1 'speed factor 1-3 (recommended value =1)
'For compatibility with the EV3 Explorer compiler,
'use array indices that are the Unicode
'code numbers of the corresponding characters.
'For example, the code number for "A" is 65
sequence[48]= "0A 1P 2c cA Cc 6c Ca 5p 1C 10" '0
sequence[49]= "0A 30 0c 0P cc 0a 4p 0A 5P 2p" '1
sequence[50]= "0A 3c 0P ca ca dC 1A 2p" '2
sequence[51]= "0A 3c 0P ca ca cA cA Ca Cp cC 10" '3
sequence[52]= "1A 0P 2p 2A 0H 0P gG 0A 2p 0a 1A" '4
sequence[53]= "0A 1C 0P Ca cA cC 1a 2a 2p 0a 4A" '5
sequence[54]= "0A 2a 0P 1c cA CA cc 2c ca cp 0c 3A" '6
sequence[55]= "1K 0P jk 6p 0e fE" '7
sequence[56]= "1C 0P cA da ca ca dA cp 0C 10" '8
sequence[57]= "0a 5C 0P cA cC 2C ca Ca cC 1p 0a 2A" '9
sequence[65]= "0A 0P 2p 0a 2P 6F ef 0f ee 2p 0A" 'A
sequence[66]= "0A 2a 0P 1C cA cC 1A 4a 5C Ca cp Cc 5a" 'B
sequence[67]= "2A 1c 0P CA cc 2c ca cp 0c 3A" 'C
sequence[68]= "0P 1C cC 2C cC 1a 8p 0a 20" 'D
sequence[69]= "0A 2a 0P 1p 0F ef 0P 6a 4A 2p" 'E
sequence[70]= "0A 2a 0P 1F 0p ef 0P 6a 4p 0A 20" 'F
sequence[71]= "0C cP 0c 1C CA cc 2c ca cp 0c 3A" 'G
sequence[72]= "0A 2a 0P 2p 0C DC 0P 4p 0a 2a 0P 4p 0A" 'H
sequence[73]= "0A 4a 0P 2p 5a 0P 4p 0A 5P 2p" 'I
sequence[74]= "0A 4a 0P 2a 3c cA Cp cC 10" 'J
sequence[75]= "0A 0P 4p 0a 2C 0P Da dp 0C" 'K
sequence[76]= "0A 4P 8a 2p" 'L
sequence[77]= "0A 0P 4C Ca cC 8p 0a" 'M
sequence[78]= "0A 0P 4E Fe 4p 8a" 'N
sequence[79]= "1C 0P cC 2C cA cC 2C cp 0C 10" 'O
sequence[80]= "0A 0P 4a 1c cA Cc 5p 0c dC" 'P
sequence[81]= "0a 5C 0P cA cC 2C ca 5C 6p 0a 1c 0P cp 0C" 'Q
sequence[82]= "0A 0P 4a 1c cA Cc 5c dp 0C" 'R
sequence[83]= "0a 5C 0P cA cA da ca cp 0c 3A" 'S
sequence[84]= "0A 4a 0P 20 5a 4p 0A 10" 'T
sequence[85]= "0a 8P 3C cA cC 3p 7P 5p 0a" 'U
sequence[86]= "0A 4J 0P Jj 0j jp 0J 8a" 'V
sequence[87]= "0A 4P 8e eE 0E Ee 4p 8a" 'W
sequence[88]= "0F 0P fp 0f 6f 0P fp 0F" 'X
sequence[89]= "1A 0P 2E ep Ee 0e 0P ep 0E 8a" 'Y
sequence[90]= "0A 4a 0P 2F Ff 2p" 'Z
sequence[32]= "20" 'space
sequence[45]= "0A 1a 0P 2p 0a 2A" 'hyphen
sequence[46]= "0P 0b 0b 0p 0B 0B 10" 'period
For j=1 To Text.GetLength(mystring)
char=text.GetSubText(mystring,j,1)
'extracts jth character from mystring
unicode_for_char=text.GetCharacterCode(char)
For n=1 to (1+Text.GetLength(sequence[unicode_for_char]))/3
movechar=text.GetSubText(sequence[unicode_for_char],n*3-2,1)
move_unicode=Text.GetCharacterCode(movechar)
If move_unicode>64 And move_unicode<91 Then 'capital letter
movesign=-1
movechar=text.ConvertToLowerCase(movechar)
Else
movesign=1
EndIf
turnchar=text.GetSubText(sequence[unicode_for_char],n*3-1,1)
turn_unicode=Text.GetCharacterCode(turnchar)
If turn_unicode>64 And turn_unicode<91 Then 'capital letter
turnsign=-1
turnchar=text.ConvertToLowerCase(turnchar)
Else
turnsign=1
EndIf
If movechar="1" Then
Motor.Move(ports,10*sf,100*s,"True")
ElseIf movechar="2" Then
Motor.Move(ports,10*sf,200*s,"True")
ElseIf movechar="3" Then
Motor.Move(ports,10*sf,300*s,"True")
ElseIf movechar="4" Then
Motor.Move(ports,10*sf,400*s,"True")
ElseIf movechar="5" Then
Motor.Move(ports,-10*sf,100*s,"True")
ElseIf movechar="6" Then
Motor.Move(ports,-10*sf,200*s,"True")
ElseIf movechar="7" Then
Motor.Move(ports,-10*sf,300*s,"True")
ElseIf movechar="8" Then
Motor.Move(ports,-10*sf,400*s,"True")
ElseIf movechar="c" Then 'move sqr(2)*100*s
Motor.Move(ports,movesign*10*sf,141.4*s,"True")
ElseIf movechar="d" Then
Motor.Move(ports,movesign*10*sf,282.8*s,"True")
ElseIf movechar="e" Then
Motor.Move(ports,movesign*10*sf,223.6*s,"True") 'sqr(5)
ElseIf movechar="f" Then
Motor.Move(ports,movesign*10*sf,447.2*s,"True") '2*sqr(5)
ElseIf movechar="g" Then
Motor.Move(ports,movesign*10*sf,316.2*s,"True") 'sqr(10)
ElseIf movechar="j" Then 'sqr(5)
Motor.Move(ports,movesign*10*sf,412.3*s,"True") 'sqr(17)
ElseIf movechar="m" Then
Motor.Move(ports,movesign*10*sf,360.6*s,"True") 'sqr(13)
EndIf
If turnchar="a" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*90,"True")
ElseIf turnchar="b" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*180,"True")
ElseIf turnchar="c" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*45,"True")
ElseIf turnchar="e" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*26.565,"True")
ElseIf turnchar="f" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*63.434,"True")
ElseIf turnchar="g" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*18.435,"True")
ElseIf turnchar="h" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*71.565,"True")
ElseIf turnchar="j" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*14.036,"True")
ElseIf turnchar="k" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*75.963,"True")
ElseIf turnchar="m" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*33.69,"True")
ElseIf turnchar="n" Then
Motor.MoveSync(ports,turnsign*15,turnsign*-15,tf*56.31,"True")
ElseIf turnchar="p" Then
Motor.Move(penport,-20*turnsign,200,"True")
'lowers pen if turnsign positive
EndIf
EndFor
Motor.Move(ports,10*sf,100*s,"True") 'space between characters
EndFor
Speaker.Tone(100,1000,1000)
I hope the above program works well for you. Feel free to modify it.
Of course, other people have made writer robots with NXT and EV3. It's interesting to compare WriterBot with others such as these:
This one is very neat and quite accurate, but limited to small text.
This one is surprisingly accurate, thanks to the use of line following to recover from errors made in the drawing of each letter.
This next one, an NTX robot, has a static base and therefore good accuracy:
Here's a more sophisticated version of the above, running under the EV3Dev operating system:
This industrial robot probably costs hundreds of times as much as an EV3 but look at that amazing precision!
This amazing sudoko solver could easily be modified to become a very accurate robot writer, but it's not a 'run-around' robot like mine and certainly isn't a simple attachment for the EV3 driving base model. In fact it's an NXT robot, which makes it all the more impressive - it even has optical character recognition! The creator has kindly made available info and building instructions at tiltedtwister.com.
This tic-tac-toe solver uses similar hardware to the previous mode but uses a more sophisticated x-y movement, with a rare part (rack 13M, part number 64781, available from bricklink.com) and one more motor needed. The software is less impressive than for the sudoko solver.
I haven't yet found a Lego writer robot other than mine that has all these features:
Allows a desired text to be entered directly into the robot and immediately drawn by the robot.
Compatibility with the standard Lego 'driving base' model
Can draw 39 different characters.
Easily scalable (change one line of code)
Uses EV3 Basic, a free program that let's you do textual programming on the EV3
Maybe it can be done with the standard EV3-G software, maybe it can't ... yes, that's a challenge to you EV3-G fans!!
By the way, I said you can rescale the characters (change their height in cm) by changing a single line of code (the line 'scale=10' near the top of the robot writer code). If you would like the user to be able to choose the scale at runtime then first delete the line 'scale=10' and then insert the following code above all the other code.
height=10 'desired character height in cm
LCD.Clear()
LCD.Text(1,0, 0,2,"Use left &")
LCD.Text(1,0, 20,2,"right keys")
LCD.Text(1,0, 40,2,"to select")
LCD.Text(1,0, 60,2,"the text")
LCD.Text(1,0, 80,2,"height (cm)")
LCD.Text(1,70,100,2,height)
Button="R" 'must give Button a value before we can refer to it
While Button<>"E"
Buttons.Wait()
Button=Buttons.GetClicks()
If Button="L" And height > 5 Then 'min height = 5cm
height=height-1
ElseIf Button="R" and height <20 then 'max height = 20cm
height=height+1
EndIf
LCD.Text(1,70,100,2,height + " ") 'think about why I added a space
EndWhile
LCD.Clear()
LCD.Text(1,0, 0,2,"Next enter")
LCD.Text(1,0, 20,2,"the desired")
LCD.Text(1,0, 40,2,"text.")
LCD.Text(1,0, 60,2,"Press any")
LCD.Text(1,0, 80,2,"button to")
LCD.Text(1,0,100,2,"continue.")
Buttons.Wait()
Buttons.Flush () 'get rid of button input
scale=height 'for compatibility with robot drawing code
You can also run the above code by itself to test it out.