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:

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.