Header Image - Randall Morgan

Blog

24 Articles

Salsa – An Operating System for Teaching – Part 1

by SysOps 0 Comments
This entry is part 1 of 3 in the series Salsa OS

It seems as one grows older you naturally take on the responsibility of teaching the next generation. Whether it be ranching, machining , or hardware/software development. What ever your background you will naturally find those younger who look to you for advice on one subject or another.

I too seem to have come to that point in my life when those younger than I seem to look to me for advice on hardware, software, and life issues. I find myself teaching how things came to be as they are now. Having some historical context helps many understand the why and how.

As you know, I’ve given many talks on hardware and software and regularly teach topics on the intersection of hardware and software to software developers. Many of these developers have little to no understanding of how software controls hardware or how hardware enables software manipulation of a system. I believe that having a basic understanding of these topics only makes for better developers. Just as I believe that having a knowledge of multiple programming languages and programming paradigms can only make you a better developer.

I have long taught that a good developer has one or two languages they are well versed in and another half dozen that they are familiar enough with to become productive in a weekend. One language doesn’t fit all problems well. In the same way a carpenter wouldn’t have a single hammer in his toolbox a software developer shouldn’t know only a single language. Who would want to write a simple text parsing script in assembly or C? Who would tackle a high speed computer vision problem in python without calling out to C/C++ libraries?

But I digress, the purpose of this series of articles is to introduce a basic Operating System for teaching OS development and the hardware boot process. As usual, I will start with some background. We will move on to a legacy BIOS (Basic Input Output System) boot system and then eventually cover the move to UEFI (Unified Extensible Firmware Interface). So let’s get started.

A Little History

To write your own OS you need to understand a few basics. We will concentrate on x86 and x86-64 hardware. My aim here is not to produce a fully functional OS that can replace Windows, Linux, or iOS. Instead, I only want to show you how to get a basic OS up and running. I’ll give you enough information and point you in the right direction so that if you’re interested, you can find further knowledge. Sometimes the trick to learning a complex subject is knowing just enough to be able to ask the right questions. But you have to have some knowledge to know what questions to ask, and where to ask them. That’s what Salsa is all about, teaching you what to ask next and help you identify where to learn more.

Before we get to far into this article let me say that for a novice developer OS development is difficult. I may leave a lot of details out that a novice may need. However, I’ll try to reduce each topic to the core essentials. Some of the material will require you to seek more information than I have provided here. At the very least you’ll need a familiarity with some kind of programming language.

The first thing we should cover is what is an operating system? In the early days of computing computers came as hardware only. You were provided a manual of the hardware and instruction set. These instructions where either binary machine code or later assembly mnemonics. The programmer had to develop an intimate knowledge of the computer’s hardware and how it all worked together. Each system was very different from the others. No one language ran on more than a single computer (later, a single computer manufacturer’s line). These computers had no operating system. When the machine was turned on, the programmer had to input a loader program that read in the program to be ran. This loader program was usually rewritten as the programs it loaded changed. Eventually, some programmers began to write more universal loaders that could load any program into the computer’s memory and then jump to the beginning of the program to start executing it.

One issue with loaders and the programs of the day was that the addresses of the various subroutines and indeed the address of the program itself was hard-coded into the program. So if the size or location of the loader changed the programs had to have all addresses recalculated. The addition of a single byte to the loader caused every line of the program to be recompiled. At this time these programs where compiled by hand! There were no compilers or interpreters.

To give you a feel for how this was done let’s look at a fictitious machine and it’s machine language:

The Fiction 1 Computational Machine

Instruction Set
Machine Code : Instruction
00000000 : No operation
00000001 : Load Accumulator A with the value following the current byte
00000010 : Load Accumulator B with the value following the current byte
00000011 : Add the values in Accumulators A and B and place the results in A
00000100 : Store the contents of Accumulator A at the memory address stored in the following byte
00000101 : Load the Accumulator A with the value stored at the address stored in the following byte
00000110 : Branch to program address stored in the following byte
00000111 : Subtract the value in Accumulator B from the value in Accumulator A and store in A
00001000 : Test Carry/Barrow Flag. The flag is set when an addition or subtraction results in a carry or barrow. When tested, sets the Accumulator A to zero if the flag was set, otherwise 255 is placed in Accumulator A.
00001001 : Branch to the address stored in the following byte if the Accumulator A contains 255
00001010 : Move the value in Accumulator A into Accumulator B
00001011 : Move the value in Accumulator B into Accumulator A

OK, looking at the machine instructions above let’s try to write a simple program that takes two numbers from memory locations 1000 and 1001 and multiplies them:

The first thing you may notice is that there is no multiplication instructions. So how do we multiply two numbers? Well, multiplication is just repetitive addition. So we simply use addition to do our multiplication. Here’s the pseudo code:
1. Load A with first value
2. Load B with second value
3. Add A and B, result is stored in A
4. Decrement B
5. Is B zero?
6. Yes, we’re done
7. No, go back to step 3

Our program:
Notes: Data Address 1011 will be used to store intermediate results
Data Address 1100 will be used for temporary storage of counter
————————————————————————
Address Instruction
00000000 00000101 # Load Accumulator A with second value
00000001 00001001 # Address of value to place in B
00000010 00001010 # Move value to B
00000011 00000101 # Load A with first value

00000100 00001100 # Address of value to place
00000100 00000011 # Add A and B and store result in A
00000101 00000100 # Save value in A
00000110 00001011 # Data Memory location to hold intermediate result
00000111 00001011 # Move value in B to A
00001000 00000010 # Load B with the value 1
00001001 00000111 # Subtract B from A and store result in A, Also sets the Carry/Borrow flag
00001010 00000100 # Save value in A
00001011 00001100 # Data Memory counter storage location
00001100 00001000 # Test flag and set A value, if flag was set, done!
00001101 00001001 # Branch if flag was cleared, destroys A
00001110 00000110 # Branch to address stored in next byte
00001111 00001110 # Forever loop… jump to self
00010000 00000101 # Retrieve counter in A
00010001 00001100 # Counter temp storage address
00010010 00001010 # Move A to B
00010011 00000101 # Retrieve current value in A
00010100 00000110 # Jump back to step 3 (Add)
00010101 00000100 # Address of step 3

The above machine program for our fictitious machine had to be hand assemble. You simply look up the value of each of the instructions in the instruction listing and then write them down. Once this was done, you then entered these values into the control panel of the machine. This was usually done via a set of switches. For our machine we would need to set 8 switches for the address and 8 more switches for the instruction byte. Then press some other switch to load the data into program memory.

Once our program is finished it simply loops at address 00001110 forever. In a real system you would store the result in a Data Memory location to be read by another program, or display it on a set of lamps. Note that all the addresses of the data and routines are hard-coded.

Eventually, programmers developed mnemonics for the machine instruction values. These came to be known as Assembly code. Assembly is a very low level abstraction on the actual machine code. A single assembly code instructions such as LDA #3 (load A with a value of 3) usually results in a single machine code instruction. However, some assembly language instructions can also result in a handful of machine instructions. Assembly language is much easier for humans to read and write. So it has become the standard language for ultra-low-level code and no-one but the chip designers use machine code anymore.

Fiction 1 Assembly Mnemonics

Mnemonic : Instruction
NOP : No operation
LDA <val> : Load Accumulator A with the value following the current byte
LDB <val> : Load Accumulator B with the value following the current byte
ADD : Add the values in Accumulators A and B and place the results in A
STA ADDR : Store the contents of Accumulator A at the memory address stored in the following byte
LDA [ADDR] : Load the Accumulator A with the value stored at the address stored in the following byte
BRA ADDR : Branch to program address stored in the following byte
SUB : Subtract the value in Accumulator B from the value in Accumulator A and store in A
TST : Test Carry/Barrow Flag. The flag is set when an addition or subtraction results in a carry or barrow. When tested, sets the Accumulator A to zero if the flag was set, otherwise 255 is placed in Accumulator A.
BRC ADDR : Branch to the address stored in the following byte if the Accumulator A contains 255
MOV A, B : Move the value in Accumulator A into Accumulator B
MOV B, A : Move the value in Accumulator B into Accumulator A

Using the assembly mnemonics our program becomes:

Address Instruction
LDA [00001001b] # Load Accumulator A with
MOV A, B # Move value to B
LDA [00001100b] # Load A with value
lp01: ADD # Add A and B and store result in A
STA [00001011b] # Save value in A
MOV B, A # Move value in B to A
LDB 00000001b # Load B with the value 1
SUB # Subtract B from A and store result in A, Also sets the Carry/Borrow flag
STA [00001100b] # Save value in A
TST # Test flag and set A value, if flag was set, done!
loop: BRC loop # Branch if flag was cleared, destroys A
LDA [0001100b] # Retrieve counter in A counter from temp storage
MOV A, B # Move A to B
LDA [00001100b] # Retrieve current value in A
BRA lp01 # Jump back to step 3 (Add

As you can see, using assembly mnemonics the code becomes much easier to read and understand. Looking through the code it doesn’t take long to understand that when a value is given in square brackets it refers to the value stored at the given memory address. If the value is not in brackets it is used as presented. These Mnemonics are much easier to remember and understand when compared to the machine code values shown above.

To see an example of what early computers were like visit: http://oldcomputers.net/altair-8800.html. You may also enjoy this video demonstration of the Altair 8800: https://www.youtube.com/watch?v=vAhp_LzvSWk. The Altair was my first computer. My father purchased a kit and put it together and allowed me to help. We then spent many hours writing simple programs for it and building our own peripherals. The Altair was the start of personal computers. While there were a couple of small computers before it (see http://www.computermuseum.20m.com/kenbak.htm), they never quite took off. The Altair however, found it’s way to the cover of Popular Electronics and found admiration from the likes of Bill Gates who wrote the first BASIC interpreter for the Altair 8800. Which resulted in the Microsoft Corporation.

Ok, so we’ve seen how it was done in the early days. Now our program doesn’t use a loader. However, for most computers loaders are required. The programmer would write a program that started just past the end of the loader program and would use addresses from that point on. Eventually, David Wheeler wrote a relocatable linker/loader for the EDSAC computer. This allowed programmers to forget about the program’s actual location in memory and let the linker/loader figure it out.

With the advent of larger more complex computer systems the need arose to have a set of routines that were permanently stored on the computer and could be called by a management program. These libraries of routines provided canned code for interacting with I/O devices like paper tape readers. They also contained the loaders to load programs from paper tape into memory and begin executing that code. These libraries grew as did the complexity and number of peripherals. Eventually, these machine were produced with a large library of hardware management routines and schedulers to allow compute jobs to be multiplexed in a time-sharing operation. It was these early libraries that eventually became the kernels of today’s operating systems.

Today’s Operating Systems now provide a host of services. However, there is still a small library of code that comes with the hardware and is responsible for initializing the hardware. This code, called the BIOS or UEFI and is executed when the hardware is first powered on. It is responsible for calling the OS loader or “Boot Loader”. The BIOS is older than the UEFI and was introduced in the first PCs back in the 1970’s. It can still be found in an emulation mode in current PCs using UEFI. UEFI was designed to replace the BIOS. It provides many more routines and services and is in step with today’s hardware. That said, we’ll first use the BIOS as it is important to understand the BIOS and how to boot using a BIOS system. In later articles we’ll examine the UEFI.

The Boot Process

The topics we’re going to cover are:

  • How a computer boots using the BIOS
  • How to write low-level programs where there is no operating system.
  • How to configure the CPU so that we can use it’s extended feature.
  • How to bootstrap code written in higher level languages so we can use it to write more complex code.
  • How to create some fundamental services such as device drivers, file systems, and multi-tasking systems.

When we boot our computer, it must start up without any notion of an operating system. Somehow, it must load the operating system. Whether it be Windows, Linux, or DOS. It must load the operating system from some form of permanent storage such as ROM, Hard Disk, Floppy Disk, SD Card, or USB thumb drive.

The pre-OS environment of our computers offers very little in the way of services. At this this stage even a simple file system doesn’t exist. What we do have is a simple collection of basic routines stored in a ROM (usually EEPROM these days). These routines provide access to the system’s basic I/O devices such as screen, keyboard, and disks.

The BIOS performs some low-level tasks such as initializing hardware and testing the basic system, it boots the operating system which is stored on a connected device such as a hard drive. However, the BIOS cannot simply load the operating system from the device. It has no idea of a file system. So the BIOS must read the operating system from a predetermined location. This location is called the Boot Sector. The boot sector is usually 512 bytes long. It is easiest for the BIOS to locate the OS at at the beginning of the disk at Cylinder 0, Head 0, Sector 0. This is the location known as the boot sector. However, most disks will not contain the OS and it would be a shame to waste this storage space on those disks when it could be used for data storage. So the BIOS needs a way to detect if an OS is present in the boot sector. To do this, the BIOS looks for a “Magic Number”, placed at the end of the 512 byte boot sector. This two byte magic number is 0xAA55. So on power up after some initialization work, the BIOS searches through the connected storage devices looking for the magic number. When it finds the magic number it knows that it has found the OS and can load it into memory and jump to the location to start execution of the OS.

If we use a program such as a Hex-Editor that will let us write raw byte values to a file, we can craft a simple boot sector.

e9 fd ff 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa

Referring to the code above, the first three bytes are a simple infinite loop in machine code. The last two bytes are the magic number. You may wonder if I got this wrong? I thought you said the magic number was AA55?

Well, I did and it is. CPU’s can store data either Lest Significant Byte (LSB) first or Most Significant Byte (MSB) First . This data storage is known as Endianess. If a CPU stores the the data LSB first, it is said to be Little-Endian. If it stores the data MSB first it is Big-Endian. The x86 CPU family is little endian and so the LSB is stored first.

Most of the time you can ignore a CPU’s endianess. However, if you have to debug compiled code in a debugger you need to know how the machine code is stored or you may completely misread it.

This program is likely the smallest program you can write. But it is a valid bootable program. We can run this program either using an emulator such as qemu or Bochs. I’ll concentrate on using Qemu here.

Creating And Booting Our Stub

First you’ll need a Hex Editor. On Linux you can install Bless Hex Editor with the command: apt install bless On Window you can install wxHexEditor. It can be downloaded from SourceForge or, you may use the hex editor of your choosing. The only requirement is that it must allow you to save the raw bytes to a file.

On Windows 10 you can use the Linux Windows Sub-System. You can find more info here: https://docs.microsoft.com/en-us/windows/wsl/install-win10 This will give you access to most of the needed Linux commands and tools used here. On Windows 10 or earlier versions such as Vista , you can install Cygwin, or MinGW to gain access to the Unix-like tools we will be using. This includes the gcc compiler and the nasm assembler.

In addition to the hex editor you will need an emulator. For Linux, MacOS, and Windows I recommend using Qemu. However, Bochs Emulator can also be used. Note that Bochs requires a configuration file to be setup before using it. Qemu will work out of the box. Qemu can be installed on Linux using the command: apt install qemu On Windows you can download the Windows installer here: https://qemu.weilnetz.de/w64/ . On Windows you’ll need to add it to your path after installation. Their is a good Youtube video on this: https://www.youtube.com/watch?v=al1cnTjeayk&t=17s. You can find the CygWin Project home page here: https://www.cygwin.com/. When installing CygWin make sure to install the gcc compiler and the nasm assembler.

Now that you have your tools installed let’s create our stub program. This program will do nothing but loop infinity. However, it will prove that we can position code and the magic number in the right places to be found and executed by the BIOS.

Open your hex editor and the three hex values e9, fd, ff into the first three locations. Next fill the editor with 00 byte values up to the 0x1ff position (511 in hex). Then replace the last two byte with the magic number remembering to place them using little-endian format. So your file should look like the binary listing above with 55, aa being the last two bytes.

Create a project folder named “salsa”. In that folder create the sub-folders salsa/scr and salsa/bin. Save your hex file as “stub.bin” in the salsa/bin folder. Next, open a terminal in salsa/bin and run the command:

$ dd if=/dev/zero of=salsa.img bs=512 count=2880

Be careful using dd as you can corrupt or even erase your hard drives using it. dd is a utility on Linux for creating disk images. It is powerful and with power comes great responsibility! Read the docs on dd and get to know it. The command I presented above takes an input file defined by the if parameter. Here we passed /dev/zero which is is a special file that provides as many null characters (ASCII NUL, 0x00) as are read from it. A typical uses is to provide a character stream for initializing data storage. Which is exactly what we use it for here.

The bs parameter tells dd the block size to produce and the of parameter is the output file name. The count parameter tell dd how many blocks to copy. If we do the math, 2880 blocks of 512 bytes gives us 1474560. This is the size we need for our 1.44 MB disk image.

Next we need to copy our binary data to the boot sector of our disk image. To do this we use the command:

$ dd if=stub.bin of=salsa.img

Now we have a 1.44 MB floppy disk image file that we can use with Qemu or Bochs. So let’s try and boot it. Use the command:

$ qemu-system-i386 salsa.img

You should see something like:

Since our stub only runs an infinite loop nothing more will happen. No matter how long you wait. So the next step will be to get our stub to do something useful. For that we’re going to need an x86 Assembler.

We’ve covered a lot in this post. So we’ll end here. Be proud that you got the BIOS to locate your code and execute it. In the next post we will learn about the processor and the x86 assembly language. We;ll need to have some basic knowledge of these topics before moving on to build something useful.

Until next time; Happy Coding!

Simple Graphics in Python – Part 3

by SysOps 0 Comments
This entry is part 3 of 3 in the series Simple Graphic in Python

Last time we left off discussing Color in John Zelle’s graphics.py library. If you didn’t catch part 1 and 2 of this series I recommend you read those parts first and then return here. You can find part 1 here: Simple Graphics in Python.

We’re almost done going over the library from a user standpoint. However, in the future I may discuss how the library works if there is enough interest. This time, we’ll be discussing window updating and animations. We’ll develop a few sample apps and have some fun. My intention here isn’t to develop full fledged apps but, rather give you a starting point for your own apps using the graphics.py library. So let’s get started.

Window Updates

The graphics.py library usually handles window updates for you anytime an object that has been drawn to the window changes. Under some circumstances it may be necessary to force a window update. For example, when using the library from some interactive shells. The window may be forced to update using the update() method on the GraphWin object. This will redraw all the items in the window.

The window auto update feature is great for simple graphics. However, as your scenes become more complex you may want to take charge and start updating the window when it bests fits your program’s schedule. This may become necessary when you are drawing many, many items to the window. You can improve efficiency by updating the window only after all items have been drawn. If you want to turn off the auto update window feature you can do so when you create the window by passing autoflush=False as in:

win = GraphWin("Window Title", 400, 400, autoflush=False)

This will disable the auto update feature and you’ll be responsible for calling win.update() when you desire to redraw all the objects in the window. Here’s an example:

"""
Prog:   ex-11_02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library turning off the 
        window's auto update feature and 
        calling win.update() yourself.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480, autoflush=False)
    
    points = []
    for i in range(1):
        for p in range(0, randint(4, 20)):
            points.append(Point(randint(0,639), randint(0,479)))
        
        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        poly = Polygon(points)
        poly.setOutline(color_rgb(r, g, b))
        poly.draw(win)

    # First mouse click adds a polygon
    points[0] = win.getMouse()  
    points[-1] = points[0] 
    poly2 = Polygon(points)
    poly2.setOutline('white')
    poly2.draw(win) 

    # Second mouse click should show the new polygon
    win.getMouse()
    update()

    # Thrid mouse click should close the window.
    win.getMouse()
    win.close()

main()

When I read the docs and implemented this program I expected that updates would only occur when the update() method was called. When this didn’t work as expected I re-read the docs and when I still couldn’t understand what was happening, I emailed John. He was kind enough to respond and set me straight about my misunderstandings. After reading his response and once again, re-reading the docs, I realized I had read, but ignored one statement in the docs. This was the cause of misunderstanding. Here’s the line I skimmed over and missed the details:

Now changes to the objects in win will only be shown when the graphics system has some idle time or when the changes are forced by a call to update().

When I read this I walked away with the impression that updates only occur when update() was called if you passed autoflush=False. However, the statement clearly says that auto-updates still occur when the system has idle time. So update is only useful if you have a blocking operation that keeps the auto-update from running.

John pointed out that in the code above, the getMouse() method calls are blocking methods but that he coded them to call update() and force drawing on the window. So my calls to getMouse don’t actually work as commented in the code above. In fact, they force an update to the window.

So with that insight let’s see if we can write a sample that actually demonstrates the use of update. Create the example below:

"""
Prog:   ex-11_03.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library turning off the 
        window's auto update feature and 
        calling win.update() yourself.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win1 = GraphWin("Rectangles", 640, 480, autoflush=False)
    #win2 = GraphWin("Rectangles", 640, 480)
    
    points = []
    for i in range(1):
        # Build up initial polygon
        for p in range(0, randint(4, 20)):
            points.append(Point(randint(0,639), randint(0,479)))
        
        r = randint(80,255)
        g = randint(0,255)
        b = randint(50,255)

        poly = Polygon(points)
        poly.setOutline(color_rgb(r, g, b))
        poly.draw(win1)

        # Loop to keep system busy
        # Since we are using a loop 
        # to delay the system you may need to increa
        max_iters = 999999999
        delay_frac = 33333333
        td = 0
        for j in range(0,max_iters):
            if j % delay_frac == 0:
                poly.undraw()
                poly = None
                r = randint(30,255)
                g = randint(30,255)
                b = randint(30,255)
                points.append(Point(randint(0,639), randint(0,479)))
                poly = Polygon(points)
                poly.setOutline(color_rgb(r, g, b))
                poly.draw(win1)
                print("Updated #", td)
                td += 1
                update() # We manually call update here, then delay again.
    
    
    # mouse click should show the new polygon
    # after busy loop completes
    win.getMouse()
    win.close()

main()

OK, with our new understanding of the autoflush=False option we’ll run the app above. You may need to adjust the value of max_iters and delay_frac as max_iters controls the total run-time of the delay loop and delay_frac controls the delay between updates during the loops.

Our program begins by creating a polygon and displaying it on the screen. Next, we enter a loop and stay in the loop for a very long time. This loop blocks the auto-update feature from updating the display. During the execution of the delay loop we check if we have made delay_frac (delay fraction) iterations since our last update. If so, the modulus expression will return 0 and the if statement will evaluate to true and we enter the if clause. Next, we erase and destroy the original polygon, and generate a new polygon using the points of last polygon with one new point added for good measure. Adding a point allows us to see the shape changed on update. The important thing to understand here is that the object isn’t being drawn to the window until we reach the update line at the bottom of the if clause.

However, if we were to forego the delay loop the auto-update feature would take over and draw the polygon when the system became idle or a method that itself (like the getMouse()) calls update() is called.

One last thing to know about the update() method is that it can take an integer parameter for the desired frame-rate. If you pass a desired frame-rate to update() it as in:

update(30)

It will update the window at this rate.

Animations

While it’s possible to use the graphics library for a GUI (Graphical User Interface), most NooBs will want to do something a bit more entertaining with it. I’m not going to tech game development here but I thought I would toss out a few example apps that I’ll intentionally leave unfinished so you, the reader, can have fun adding features and completing the demo apps.

There are a few ways to accomplish animation on a computer. The most often used is motion animation where an object is moved into it’s new location, then drawn, then erased and moved again. This cycle is known as the animation loop, or if you’re a gamer, the game loop.

Handball

Our first animation is a simple Pong-like game (remember these apps will be unfinished and incomplete) that simply draws a circle and a rectangle on the screen then moves them around the screen.

Screen Shot of the Handball App

We will use an OOP (Object Oriented Programming) approach for the Handball app.

#!/usr/bin/env python3

"""
Prog:   ex-12_01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to do simple
        animation of a pendulum.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from math import *

width = 640
height = 480


class Paddle():
    def __init__(self, x, y, win):
        self.x = x
        self.y = y
        self.w = 10
        self.h = 50
        self.win = win
        self.rect = Rectangle(Point(self.x, self.y), Point(self.x+self.w, self.y+self.h))
        self.rect.setFill('white')
    
    def getX(self):
        return self.rect.p1.getX()

    def getY(self):
        return self.rect.p1.getY()

    def getW(self):
        return self.w

    def getH(self):
        return self.h
        
    def move(self, xspeed, yspeed):
        self.rect.move(xspeed, yspeed)

    def draw(self):
        self.rect.draw(self.win)

    def undraw(self):
        self.rect.undraw()

  
class Ball():
    def __init__(self, x, y, win):
        self.x = x
        self.y = y
        self.xspeed = 3
        self.yspeed = 1
        self.r = 10
        self.win = win
        self.cir = Circle(Point(self.x, self.y), self.r)
        self.cir.setFill('white')

    def set_speed(self, xspeed, yspeed):
        self.xspeed = xspeed
        self.yspeed = yspeed
    
    def move(self):
        self.cir.move(self.xspeed, self.yspeed)
        p1 = self.cir.getP1()
        self.x = p1.getX()
        self.y = p1.getY()
        
    def draw(self):
        self.cir.draw(self.win)

    def undraw(self):
        self.cir.undraw()

    def check_collision(self, pad):
        print("Xspeed: ", self.xspeed, "Yspeed: ", self.yspeed) 
        xbound = self.within_x_bounds(pad)
        ybound = self.within_y_bounds(pad)

        if xbound and ybound:
            if xbound:
                self.xspeed = -self.xspeed
            if ybound:
                self.yspeed = -self.yspeed
            self.move()
            return True

        return False
        

    def within_x_bounds(self, pad):
        if self.xspeed < 0:
            if (self.x < pad.getX() + pad.getW()) and (self.x > pad.getX()):
                return True
            else:
                return False
        else:
            if (self.x + self.r >= pad.getX()) and (self.x <= pad.getX() + pad.getW()):
                return True
            else:
                return False
    
    def within_y_bounds(self, pad):
        if self.y+self.r >= pad.getY() and self.y <= pad.getY()+pad.getH():
            return True
        else:
            return False
    
    def check_edges(self, width, height):
        p1 = self.cir.getP1()
        if p1.getX() < 0 or p1.getX()+self.r > width:
             self.xspeed = -self.xspeed;
        if p1.getY() < 0 or p1.getY()+self.r > height:
            self.yspeed = -self.yspeed;
  
      
def main():
    win = GraphWin("Handball", width, height)
    win.setBackground('black')

    xspeed = 0.1
    yspeed = 0.1

    # initial placement of paddle  
    pad = Paddle(10, (height/2)-25, win) 
    # Draw paddles.
    pad.draw()

    ball = Ball(width/2, height/2, win)
    ball.set_speed(xspeed, yspeed)
    ball.draw()
   
    while 1:
        # get imput if any
        k = win.checkKey()
        if k == 'a':
            if pad.getY() < 0:
                pad.move(0, 0) 
            else:
                pad.move(0, -20)
            print('Pad1: Move Up', pad.getX(), pad.getY())

        if k == 'z':
            if pad.getY() > height - 50:
                pad.move(0, 0)
            else:
                pad.move(0, 20)
            print('Pad1: Move down', pad.getX(), pad.getY())

        ball.check_edges(width, height)
        ball.move()

        if ball.check_collision(pad):
            print("Ball hit paddle")

    win.getMouse()
    win.close()


main()

This may not be the most efficient implementation however, it is only meant to provide you with some inspiration for creating your own apps by demonstrating what can be accomplished using the graphics.py library.

If you scan the code you quickly see that we have a Paddle calls, a Ball class, and a main function. Our Paddle object encapsulates properties (data) and methods (actions) our paddle can take. Our paddle needs to keep track of it’s position (x,y) and size (w,h). When we create a Paddle object from the class (a class is a blueprint for the object we want to create) we pass in these values along with the window we want the paddle to draw itself on. We save the window for future use as we will always draw the paddle to the same window. So saving it here simplifies our code and we no-longer have the need to pass the window each time we call draw() on the paddle.

When a paddle is instantiated (an object is created from the class), Python calls the __init__() method. In this method you place all the code that you need to run to set things up for use. So we create the rectangle that will represent our paddle on the screen. We also set the fill color on the paddle to white. Our paddle is now ready for use.

Often you’ll need to have access to the state of an object. Later, we’ll need to be able to determine if the ball hits our paddle so, we need access to the location and size of our paddle. Do enable this we provide accessor methods getX(), getY(), getW(), getH(). These return the paddles x, y, width, and height respectively.

Our paddle also needs to move up and down so we can hit the ball as it bounces across the court. So, well need a move() method. There may be times we want to move at different speeds. So will pass in the xspeed and yspeed for our paddle. You may be wondering why we need the xspeed. Truly we don’t. We could just hard code the xspeed in our class code. But that would restrict us to moving only in the Y plane. Yes, it’s true that the paddle in Pong moves only in the Y play (up and down). But think how much fun it would be to animate the paddle to shake when the ball hits it. Here, we would need access to the yspeed to accomplish this. Including it also opens the class up for reuse. For example, suppose you want to use the paddle in a falling object game. If we didn’t include the yspeed here, you wouldn’t be able to.

In almost all motion animations each object will need to complete the three tasks of the animation loop, Move, Draw, Erase, Repeat… The graphics library actually takes care of this for use in the move() method of the various shape objects. So we really don’t need to worry about it. But you do need to know it’s happening under the hood.

we’ll add a draw() method to our paddle. Here we only need to call draw on the rectangle that represents our paddle on on the display. We may also need an erase method at some point. So, we’ll include it here and again however, we’ll call it undraw() to stay consistent with the library methods. All we need to do in the undraw() method is to call it’s namesake on the rectangle that represents our paddle.

The Ball class is a bit more complicated. Mostly because we encapsulated the logic of what to do when the ball comes in contact with another object. For example, if the ball hits the edge of the screen or the paddle. The balls move method is a bit different than the paddle’s move method. This is because it is expected that once the ball is moving it will keep moving. Also, we don’t want to have to changed the balls direction ourselves. We want it to include this action when it hits an object so it bounces off on it’s own. So in the Ball class we provide an xspeed and yspeed and set default values for them. Our ball is represented by a circle on the screen. So we have to create a circle and save it. We laos set the fill color in the __init__() method.

We may need to change the ball’s speed so we include a set_speed() method. We will also need to know when the ball has hit the edge of the court. This is handled in the check_edges() method. Here you’ll need to pass in the courts with and height. It is assumed that the upper right corner of the court is (0,0) and all calculations make this assumption.

The check_collision() method is passed the paddle object to test for collision with the ball. The ball object includes two helper methods, within_x_bounds() and within_y_bounds() to check if the ball is within the bounds of the paddle object.

To make this a complete game you need to add scoring and allow the ball to reset and be re-served if it passes the paddle. You can also use this as the basic frame work for Pong by adding another paddle and additional input handling for another player. Just a hint if you try this, google keyboard input methods for python before you attempt this. As, the current approach wont report multiple key presses at once. Their are solutions but I’ll leave that as an exercise for the reader.

Simulations

Games and GUIs aren’t the only things that graphics can help with. Graphics are often used to convey information about some chemical or mathematical process. Let’s take a simple case, that of calculating pi. It is well known that PI can be estimated to surprising accuracy by randomly throwing darts at art board. OK, so it’s a bit more complex than that but, only a little. First, what we really need is a circle inside a square. The circle’s diameter must fit snugly inside the square. More precisely the diameter of the circle must equal the length of one side of the square.

The logic is simple: If the circle’s diameter is equal to the square’s length, than the area of the circle should be equal to: (area of the square / area of circle)*4. To learn more about this you can checkout this link: https://www.youtube.com/watch?v=M34TO71SKGk

We can draw circles, squares, and points (to represent darts) using the graphics.py library. So all we need to do is draw a circle inside a square and throw darts at it, then calculate the ratio of darts that landed in the circle to the total number of darts thrown. We will simply plot random points for our darts and keep track of how many we throw and where they landed. Let’s see how we might do this in python:


#!/usr/bin/env python3
"""
Prog:   ex-13.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to visualize the
        process of estimating pi by randomly
        throwing darts.

Lic:    This code is placed in the public domain.

"""
from graphics import *
from random import *
from math import *

width = 400
height = 400
center = width/2
r = width/2

# Find the deststance between two points
def dest(x1,y1, x2,y2):
    return sqrt((x1 - x2)**2 + (y1 - y2)**2)


def main():
    win = GraphWin("Pi Estimation", width, height)
    # Draw a square
    sq = Rectangle(Point(0,0), Point(width-1,height-1))
    sq.setOutline("blue")
    sq.draw(win)
    # Draw a circle fitting the square
    c = Circle(Point(center,center), r)
    c.setOutline("white")
    c.draw(win)

    darts_thrown = 0
    darts_in_circle = 0
    best = 0

    estimate = 0
    best_estimate = 0
    for i in range(1,100000):
        x = randint(0, 400)
        y = randint(0, 400)
        p = Point(x,y)
        darts_thrown += 1
                
        # Is are point in the circle?
        if(dest(center,center, x, y) < r):
            darts_in_circle += 1
            p.setFill(color_rgb(220,200, 120))
            p.draw(win)
        else:
            p.setFill(color_rgb(127, 200, 127))
            p.draw(win)
        
        if i % 3000 == 0:
            estimate = (darts_in_circle/darts_thrown)*4
            if abs(pi - estimate) < abs(pi - best_estimate):
                best_estimate = estimate
            
            print("Iteration: ", i, " Estimated PI: ", best_estimate)

    print("Done!")

    win.getMouse()
    win.close()


main()

If you run this code you should get a printed output of something like this:

Iteration: 3000 Estimated PI: 3.0893333333333333
Iteration: 6000 Estimated PI: 3.0893333333333333
Iteration: 9000 Estimated PI: 3.089777777777778
Iteration: 12000 Estimated PI: 3.0936666666666666
Iteration: 15000 Estimated PI: 3.1018666666666665
Iteration: 18000 Estimated PI: 3.110222222222222
Iteration: 21000 Estimated PI: 3.1125714285714285
Iteration: 24000 Estimated PI: 3.1161666666666665
Iteration: 27000 Estimated PI: 3.1161666666666665
Iteration: 30000 Estimated PI: 3.1161666666666665
Iteration: 33000 Estimated PI: 3.1161666666666665
Iteration: 36000 Estimated PI: 3.1172222222222223
Iteration: 39000 Estimated PI: 3.12174358974359
Iteration: 42000 Estimated PI: 3.12174358974359
Iteration: 45000 Estimated PI: 3.1226666666666665
Iteration: 48000 Estimated PI: 3.12475
Iteration: 51000 Estimated PI: 3.124941176470588
Iteration: 54000 Estimated PI: 3.124941176470588
Iteration: 57000 Estimated PI: 3.124941176470588
Iteration: 60000 Estimated PI: 3.124941176470588
Iteration: 63000 Estimated PI: 3.124941176470588
Iteration: 66000 Estimated PI: 3.1267878787878787
Iteration: 69000 Estimated PI: 3.1267878787878787
Iteration: 72000 Estimated PI: 3.1267878787878787
Iteration: 75000 Estimated PI: 3.1267878787878787
Iteration: 78000 Estimated PI: 3.1267878787878787
Iteration: 81000 Estimated PI: 3.1267878787878787
Iteration: 84000 Estimated PI: 3.1267878787878787
Iteration: 87000 Estimated PI: 3.1267878787878787
Iteration: 90000 Estimated PI: 3.1267878787878787
Iteration: 93000 Estimated PI: 3.1267878787878787
Iteration: 96000 Estimated PI: 3.1267878787878787
Iteration: 99000 Estimated PI: 3.1267878787878787
Done!

I ran this program several times and the best I did was 3.1419. Which is pretty good given the fact that our random number generator is actually a pseudo random number generator. I also believe that the math library in python may be rounding our calculations. Using a more precise math library would improve the estimate. However, this app is only meant to demonstrate the process. So, I’ll leave implementing a more precise version up to the reader.


Screen Shot of Pi Estimator App

Running the application longer with more dart throws will improve the estimation of PI.

Multiple Window

The graphics.py library allows you to have multiple windows. This can be handy for both GUIs and data visualization. You could for example display the plot of darts in a PI estimation program in one window while plotting the error on a graph in another window.

You might wonder why you would ever need more than one window. Well, how often do you use a drop down menu? The drop down menu is actually a small window with a list of items that is placed over the main window. Dialog boxes, popups, etc… are all windows. So being able to create additional windows comes in very handy for GUI applications. However, other types of applications can make use of multiple window. Take our PI estimating application above. We could use an additional window to plot the standard deviation of our a current estimate. Using multiple windows you can show many plots at the same time. This would allow the user to correlate the information in the various plots.

I’m going to show you a simple demo that is once again, an incomplete game. This game is a two player version of Battleship. It has several issues left for you to resolve. however, it does demonstrate the use of two windows being used in a single application. The code here is a bit longer than our other applications and I would say this code is in a pre-alpha state. It is only meant to ignite you imagination and give you a base from which to work to complete the game.

I’m sure I don’t have to explain how Battleship is played. However, if you need and explanation, google “battleship game” and you’ll find a wikipedia article on it. Let’s see some code:


#!/usr/bin/env python3
"""
Prog:   ex-14.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library and the use of
        multiple windows in a single app.

Lic:    This code is placed in the public domain.

"""
from graphics import *
from random import *
from math import *

width = 400
height = 400



class Board():

    def __init__(self, title, width, height):
        self.xsize = 10
        self.ysize = 10
        self.w = width
        self.h = height
        self.grid = []
        self.win = GraphWin(title, width, height)
        self.vessels = []


    # returns pixels per division
    def xdiv(self):
        xdiv = self.w / self.xsize
        return xdiv


    # returns pixels per division
    def ydiv(self):
        ydiv = self.h / self.ysize 
        return ydiv


    def rowcol_to_xy(self, r, c):
        y = int(r * self.ydiv())
        x = int(c * self.xdiv()) 
        return (x, y)


    def rowcol_to_point(self, r, c):
        p = self.rowcol_to_xy(r, c)
        return Point(p[0], p[1])


    def xy_to_rowcol(self, x, y):
        r = int(y / self.ydiv())
        c = int(x / self.xdiv())
        return (r, c)
    

    # return the coordinates in pixels for
    # the center of the cell at (row, col)
    def center_xy(self, r, c):
        # calc (x,y) position of upper left
        # corner of cell at (r,c)
        xy1 = self.rowcol_to_xy(r, c)
        # Calculate lower right corner
        xy2 = self.rowcol_to_xy(r+1, c+1)
        
        # find the middel of the cell
        dx = (xy2[0] - xy1[0]) - self.xdiv() / 2
        dy = (xy2[1] - xy1[1]) - self.ydiv() / 2
        cx = dx + xy1[0]
        cy = dy + xy1[1]

        return (cx, cy)


    def dist(self, x1, y1, x2, y2):
        return sqrt(((x2-x1)**2) + ((y2 - y1)**2))


    # draws the grid of cells on the board
    def draw(self):
        # Expects (0,0) to be located in the upper left
        xdiv = self.w / self.xsize
        ydiv = self.h / self.ysize
        for i in range(0, self.w, int(xdiv)):
            l = Line(Point(i, 0), Point(i, self.h))
            l.draw(self.win)

        for j in range(0, self.h, int(ydiv)):
            l = Line(Point(0, j), Point(self.w, j))
            l.draw(self.win)


    # place a vessel on the board
    def place(self, vessel):
        plX = vessel.row * self.ydiv()
        plY = vessel.col * self.xdiv()
        if vessel.rect == None:
            rowcol = self.rowcol_to_xy(vessel.row, vessel.col)
            x1 = rowcol[0]
            y1 = rowcol[1]
            if vessel.horz:
                x2 = x1 + (vessel.length * self.xdiv())
                y2 = y1 + self.ydiv()
            else:
                y2 = y1 + (vessel.length * self.ydiv())
                x2 = x1 + self.xdiv()
            vessel.rect = Rectangle(Point(x1, y1), Point(x2, y2))
        vessel.rect.setOutline(color_rgb(127,220,127))
        vessel.rect.draw(self.win)


    # tests to see if the vessels on this board
    # have been hit by the shot taken, and call
    # draw_hit() to mark the shot with a red X 
    # in the cell where it landed.
    def hit(self, loc):
        col = int(loc.getX() / (self.w / self.xsize))
        row = int(loc.getY() / (self.h / self.ysize))
        self.draw_hit(row, col)


    # draws the actual red X, called by hit()
    def draw_hit(self, row, col):
        xy1 = self.rowcol_to_xy(row, col)
        x1 = xy1[0]
        y1 = xy1[1]
        xy2 = self.rowcol_to_xy(row+1, col+1)
        x2 = xy2[0]
        y2 = xy2[1]
        
        p1 = Point(x1,y1)
        p2 = Point(x2, y2)
        p3 = Point(x1,y2)
        p4 = Point(x2, y1)

        l1 = Line(p1, p2)
        l2 = Line(p3, p4)
        l1.setOutline('red')
        l2.setOutline('red')
        l1.draw(self.win)
        l2.draw(self.win)


    # Use to mark the shooter's board
    # for shots taken. So the player may
    # know where they have already shot
    def mark(self, r, c):
        c = self.center_xy(r, c)
        print("Center of mark, x: " + str(c[0]) + ", y: " + str(c[1]))
        pc = Point(c[0], c[1])
        cir = Circle(pc, int(self.xdiv()/2))
        cir.setOutline(color_rgb(50, 50, 200))
        cir.draw(self.win)



# Simple vessel class
class Vessel():

    def __init__(self, name, row, col, length, place_horz):
        self.row = row
        self.col = col
        self.length = length
        self.horz = place_horz
        self.name = name
        self.hit_count = 0
        self.rect = None # created in board.place()

        if self.name == 'Carrier':
            self.makeCarrier()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Battleship':
            self.makeBattleship()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Cruiser':
            self.makeCruiser()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Submarine':
            self.makeSubmarine()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        elif self.name == 'Destroyer':
            self.makeDestroyer()
            print("Row: " + str(self.row))
            print("Col: " + str(self.col))
        else:
            print('Illegal Vessel Type: "'+name+'" not defined')
            return None 


    def makeCarrier(self):
        if self.name != 'Carrier':
            return
        elif self.horz:
            self.col = randint(0, 4)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 4)


    def makeBattleship(self):
        if self.name != 'Battleship':
            return
        elif self.horz:
            self.col = randint(0,5)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 5)


    def makeCruiser(self):
        if self.name != 'Cruiser':
            return
        elif self.horz:
            self.col = randint(0,6)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 6)


    def makeSubmarine(self):
        if self.name != 'Submarine':
            return
        elif self.horz:
            self.col = randint(0,6)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 6)


    def makeDestroyer(self):
        if self.name != 'Destroyer':
            return
        elif self.horz:
            self.col = randint(0,7)
            self.row = randint(0, 9)
        else:
            self.col = randint(0, 9)
            self.row = randint(0, 7)


    def getName(self):
        return self.name


    def move(self, x, y):
        self.rect.move(x, y)


    def draw(self):
        self.rect.draw()   


    # Not Yet Implemented
    # Given a row, col value for
    # a shot, return true if the
    # vessel was hit by shot 
    def hit(self, r, c):
        return False
    


# Simple player class
class Player():

    def __init__(self, name, width, height):
        self.name = name
        self.board = Board(name, width, height)

        # Create fleet
        self.Carrier = Vessel('Carrier', randint(0,4), randint(0, 9), 5, True)
        self.Battleship = Vessel('Battleship', randint(0,5), randint(0,5), 4, False)
        self.Cruiser = Vessel('Cruiser', randint(0,4), randint(0,4), 3, True)
        self.Submarine = Vessel('Submarine', randint(1,4), randint(1,4), 3, True)
        self.Destroyer = Vessel('Destroyer', randint(1,4), randint(1,4), 2, True)


    def getName(self):
        return self.name


    def getMouse(self):
        return self.board.win.getMouse()


    # called when player should take turn
    def turn(self, board):
        loc = self.getMouse()
        board.hit(loc)
        rc = self.board.xy_to_rowcol(loc.getX(), loc.getY())
        self.board.mark(rc[0], rc[1])
        

    # Not Yet Implemented
    # Should test if the player's
    # entire fleet has been sunk,
    # if so, game over!
    def fleetSunk(self):
        return False
        pass


    def close(self):
        self.board.win.close()


    # Initialize fleet
    def draw(self):
        self.board.draw()
        self.board.place(self.Carrier)
        self.board.place(self.Battleship)
        self.board.place(self.Cruiser)
        self.board.place(self.Submarine)
        self.board.place(self.Destroyer)



def main():
    # Open game boards
    player1 = Player("Player 1", 400, 400)
    player2 = Player("Player 2", 400, 400)
    player1.draw()
    player2.draw()

    while ~player1.fleetSunk() and ~player2.fleetSunk():
        player1.turn(player2.board)
        player2.turn(player1.board)

    player1.getMouse()
    player1.close()
    player2.close()


main()
   

Looking over this code we can see it is really rather simple. In main() we create two players and call draw() on them. Next, we enter a while loop. This loop will loop forever as I left the fleetSunk() method unimplemented. It is hard coded to return false. I’ve left implementing this method up to the reader.

Within the loop we call turn on each player passing in the opponent’s game board. Each game board is responsible for calculating it’s own size, and completing all drawing operations on it’s grid.

The player.turn() method takes the opponent’s game board as a parameter and and after getting the mouse click location, passes that location to the opponent’s board.hit() method. Next, we convert the pixel (x,y) values to (row, column) values and pass those to our own board’s mark() method to draw a blue circle to indicate where we’ve taken shots. You could leave this set out or toggle it to make the game more challenging.

The board’s hit method is not implemented in this code and is also left as an exercise for the reader. However, it should take the row, column values passed in and determine if any of the vessels on it’s board have been hit. If so, it should mark that vessel as damaged and increment the vessel.hit_count. This should be done by calling the vessel’s hit() method. The vessel is sunk if the hit_count matches the vessel’s length.

The player’s fleetSunk() method should simply test if all the player’s vessels have been sunk and return true if they have.

I’m leaving the completion of this up to the readers. You’ll most likely want to add some type of scoring. You might even change the X draw for hit’s and the circle drawn as a marker, to an image of an explosion and a slash in the water respectively. You might also make the shooter’s board indicate whether the shot was a hit or a miss. You should have all the tools you need to implement these features. If you take a little time and analyze each class and each of it’s methods, you should have little trouble.

There is one issue that this code has I didn’t have time to correct. That is that the vessels are drawn at random locations and therefor often overlap each other. This isn’t good, as one shot can damage two vessels. This might be allowed if this were Angry Birds (two bird, one shot…). However, it’s Battleship! SO you’ll need to implement some method for ensuring that all vessels are placed in such a manner that they wont overlap. You can find one such solution here: https://stackoverflow.com/questions/3265986. This isn’t the only solution but it’s one that isn’t too hard to implement. Do note that one issue with this method is that everything is placed around a focal point that will never be occupied by a vessel. Effectively ensuring that the center call of the board will always be empty. This could be dealt with by shrinking the field for the purpose of the placement calculation and then randomly shifting it up or down one row.

Good luck! If you have questions of comments I’d enjoy hearing from you.

Simple Graphics in Python – Part 2

by SysOps 0 Comments
This entry is part 2 of 3 in the series Simple Graphic in Python

We left off the last time discussing how to use the Text and Entry objects in John Zelle’s graphics.py library from his book and course Python Programming an Introduction to Computer Science. If you missed the first installment check it out here: http://www.randallmorgan.me/blog/simple-graphics-in-python/.

This time we will cover a few topics I skipped over such as drawing Rectangles and Ovals. As we did in the first part, we’ll draw these using points or lines first, and then introduce the library methods. This gives you the opportunity to appreciate what the library does for you and gives you the insight needed if you desire to change a method or extend the library by mucking around in it. From what I’ve seen of John on https://www.youtube.com/results?search_query=John+Zelle and at talks, I suspect he would invite you to muck around and make it your own.

Drawing Rectangles

So let’s start with rectangles. A rectangle is simply a four sided object with right angles. A square is a special rectangle in which all sides are of equal length. However, not all rectangles are not squares. Most are not in fact. So we saw in part one how to draw lines using points, our most basic visual object. So we will skip to drawing rectangles using lines and forego the lower level drawing with points.

Notice that in all of computer science, and indeed in all forms of engineering, complex objects are built up from simpler objects. We started with points and turned them into lines and circles. Now we’ll turn lines into rectangles and polygons and circles into ovals.

Our draw_rect function will take five parameters. First, we need the x and y positions of the upper left corner of the rectangle. Next, we need the width and height. Finally, we need the window to draw the rectangle into. Given these parameters we can draw rectangles of all sizes.

Take a look at the code below. Create a finle called ex-08_01.py and enter the following code:

"""
Prog:   ex-08_01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        rectangles to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def draw_rect(x, y, w, h, win):
    # Build points for the rect's corners
    p1 = Point(x,y)
    p2 = Point(x+w, y)
    p3 = Point(x+w, y+h)
    p4 = Point(x, y+h)
  
    # draw the lines to connect the points
    lines = []
    lines.append(Line(p1, p2))
    lines.append(Line(p2, p3))
    lines.append(Line(p3, p4))
    lines.append(Line(p4, p1))

    # set the line color
    # and draw
    for ln in lines:
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        ln.setFill(color_rgb(r, g, b))
        ln.draw(win)
    
    
    
def main():
    win = GraphWin("Rectangles", 400, 400)

    for i in range(0,50):
        x = randint(0, 400)
        y = randint(0, 400)
        w = randint(0,400)
        h = randint(0,400)
        draw_rect(x, y, w, h, win)

    win.getMouse()
    win.close()

main()

In the main() function we create a loop to draw 50 rectangles using our draw_rect() function. We generate the random position and size and call draw_rect() with them also passing along the window we want them drawn on.

In our draw_rect function() we first generate the points that form the four corners of the rectangle. Then we generate a list of lines passing the points as needed to draw the rectangle, being sure to close the rectangle by connecting the first and last points with the final line. In the loop we set the fill color and draw each line. It’s all very straight forward. Looking back at the line function in the first part of this article, you can see how we might draw rectangles using points if we didn’t have a line function.

Ok, the graphics.py library has it’s own Rectangle() method. I’ll demonstrate using it next.

"""
Prog:   ex-08_02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        rectangles to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480)
    
    for i in range(1000):
        p1 = Point(randint(0,639), randint(0,479))
        p2 = Point(randint(0,639), randint(0,479))

        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        rect = Rectangle(p1,p2)
        rect.setOutline(color_rgb(r, g, b))
        rect.draw(win)

    win.getMouse()
    win.close()

main()

Here you can see that the library method takes two points that define the upper left and lower right corners of the rectangle. Our code above then set’s the Outline color to a randomly generated color and draws the rectangle. Nothing special going on here.

There really isn’t a need to include a method for squares unless you need to draw a lot of them. If you do, then it may be worth including a method just for squares. Such a method might look like this:

def square(x, y, side_length, win):
    p1 = Point(x, y)
    p2 = Point(x+side_length, y+side_length)

    r1 = Rectangle(p1, p2)
    r1.draw(win)

If you wanted to keep the same API as the graphics.py library, all of our functions could just return the composed type to the caller so the draw and other methods could be called on it. Then the caller would be in control of calling the draw method where and when it needed to. I changed the API on our functions mainly to differentiate them from the library methods.

Drawing Ovals

OK, next up is Ovals. Just a squares are a special case of rectangles, circles are a special case of ovals. So why didn’t the library call it’s circle method ovals and allow us to simply pass like parameters as we do to get a square from the Rectangle() method? I suspect this inconsistency was mostly for convenience.

Recall how we calculated the (x, y) positions on the circumference of the circle the using cos() and sin() functions? We used a loop that produced a value between 0 and 360 and feed that to the cos and sin functions for the x and y positions. However, because we needed a circle greater than a unit in radius, we had to scale the (x, y) values by the desired radius.

Now ask yourself what happens if the scale factor for x and y are not identical? Would we still get a circle? No, we would get a circle that was squished or expanded in one direction. The would result in an oval being drawn. See the following code in ex-09_01.py:

#!/usr/bin/env python3

"""
Prog:   ex-09_01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw ovals 
        using points.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from math import *
from random import randint

# Given the center x, center y
# and radius, draw a circle 
# using points.
def oval(cx, cy, rx, ry, color, win):
    
    for i in range(0, 360):
        x = cos(i)*rx + cx;
        y = sin(i)*ry + cy

        p = Point(x,y)
        p.setFill(color)
        p.draw(win)


def main():
    win = GraphWin("Circles", 400, 400)
    win.setBackground(color_rgb(0,0,0))

    for i in range(0, 20):
        rx = randint(10, 200)
        ry = randint(10, 200)

        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255) 
        oval(200, 200, rx, ry, color_rgb(r, g, b), win)
   
    win.getMouse()
    win.close()


main()

Run the code above. You should see some attractive ovals… Compare the code here to the circle code we used in the first part of the series.

OK, now let’s see how the library does ovals. Run the code below:

"""
Prog:   ex-09_02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        ovals to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480)
    
    for i in range(0,50):
        p1 = Point(randint(0,639), randint(0,479))
        p2 = Point(randint(0,639), randint(0,479))

        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        rect = Oval(p1,p2)
        rect.setOutline(color_rgb(r, g, b))
        rect.draw(win)

    win.getMouse()
    win.close()

main()

Here again we see that the oval method contains the same API calls as the Circle method. The library’s Oval() method takes different parameters than our function. It take a bounding box, a rectangle made of two points that enclose the desired oval. However, it produces an oval none the less.

Polygons

Alright, we’ve covered points, lines, rectangles, squares, circles and ovals, not to mention text. What else can we do with the library? Well, we could build up a collection of points and connect them with lines to build polygons. However, we wont write the code ourselves for this. John’s library already contains a Polygon method and if you’ve made it this far, you can infer how to develop such a function. So I’ll just demonstrate how to use the method from the graphics.py library:


"""
Prog:   ex-10.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for drawing
        polygons to the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    win = GraphWin("Rectangles", 640, 480)
    
    for i in range(10):
        points = []
        for p in range(0, randint(4, 20)):
            points.append(Point(randint(0,639), randint(0,479)))
        
        r = randint(0,255)
        g = randint(0,255)
        b = randint(0,255)

        poly = Polygon(points)
        poly.setOutline(color_rgb(r, g, b))
        poly.draw(win)

    win.getMouse()
    win.close()

main()

Loading Images

The graphics.py library contains a method for loading images. However, the formats support depend on your system and it’s setup. Most systems should handle working with PPM and GIF formats. Some may even handle PNG and TIFF formats. Installing PIL (Photo Imaging Library) on your system may get you additional formats. John’s graphics.py library uses the TK or more formally Tkinter library under the hood. graphics.py is really just a lite weight wrapper to make using Tk easier. So if you can get Tk to support a new image format, you can probably get the graphics.py library to support it.

All images support the generic methods of move(), draw(), undraw(), and clone(). In addition there is the Image() method for creating an Image object. You need to pass it an anchor point and a filename. The constructed image will be centered at the given anchor point. You may also call this method passing the width and height along with the filename.

You can call the getAnchor() to get the Point where the image is anchored. Calling getWidth() and getHeight() do exactly as you would expect and return the width and height of the image respectively.

A fun and functional method is the getPixel method that let’s us retrieve the pixel in the image at location (x, y). This method returns the color as a list of (r,g,b) color values. Note that the pixel location is relative to the image it’s self and must be inside the image.

The getPixel method can help us do some pretty cool things when paired with the setPixel() method. This method takes a color_rgb() value.

Lastly, the library can save images to a file. The format saved is determined by the filename extension given. You can perform the save operation by calling the save() method.

Now let us see how simple the graphics.py library make loading an image. Create a new file and enter the program below.


"""
Prog:   ex-10.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library for images into
        the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *

def main():
    win = GraphWin("Image Loader", 640, 480)
    win.setBackground(color_rgb(0,0,0))

    p1 = Point(320, 240) # Point of center of image
    img = Image(p1, 'images/PixelCar.gif')
    img.draw(win)

    win.getMouse()
    win.close()

main() # Call main()

Now before we can run this program we need and image. You can down load the one I used here: http://www.randallmorgan.me/wp-content/uploads/2018/12/PixelCar.gif

Download this file and save it. I placed my copy in a sub-directory of my program’s directory names images. So the path to the image in the code reflects this. If you place the image in the same directory as your code, you’ll need to remove the ‘image/’ part of the filename and just pass ‘PixelCar.gif’.

Once you have the image and have modified the path in the code to point to your image, it is time to run the application. You should see the car image in the middle of the screen.

Let’s take a short detour and talk about color next.

Color

Throughout this series we’ve been using the color_rgb() method to generate colors. However, the library is based on the TKinter library and it supports named colors. Specifically, X11 named colors. We can easily pass these color names to any method that takes a color_rgb() value. I tend to use the color_rgb() method as it gives more control over the color. However, it can be very convenient to simply pass the color name for things like background colors or well defined colors such as white. You can learn more about X11 Color names here: http://cng.seas.rochester.edu/CNG/docs/x11color.html

Rather than writing an app to demonstrate the use of color names, just take the the image app above (ex-11_01.py) and change the color in the win.setBackground() method to ‘white’. Once you made the change run the program. Go ahead and try some of the other color names you find at the link above.

OK, we’ve covered a lot of material in this issue. In my next installment I’ll cover how to force a window update and animation.

Keep Coding!

Simple Graphics in Python

by SysOps 0 Comments
This entry is part 1 of 3 in the series Simple Graphic in Python

For the beginning Python programmer, getting started with command line applications is the first step. At some point however, even a NooB will get bored with the terminal window and desire something more. Yet, many of the graphics and gui libraries for Python can be overwhelming for a novice. Prof. John Zelle saw the need for a simple graphics library for his students and went searching. He didn’t find anything he felt was suitable for the truly novice python student. So, he created a new graphics library to fill this niche’. John’s library was used in his book “Python Programming An Introduction to Computer Science”. Which has seen three editions so far.

John’s library is great for teaching and keeping students interested. It’s simple, lacks complex features, and is straight-forward to use. John has provided great documentation. However, many new pythonistas will desire examples.

In searching for examples I came across a short series on youtube.com.   That series, though well done and well received , was also incomplete. There had been sever posts requesting updates but those have yet to surface. For that reason I decided to do a series of articles showing some sample code and just what can be accomplished with John’s great little library. So here goes:

As a software developer you must get used to reading documentation. You can find the documentation for John’s library at : http://mcsp.wartburg.edu/zelle/python/. The best thing you can do for yourself is read this documentation. It is short, only 8 pages. Then come back here and follow along with the examples.

Before we can start drawing on a window, we need to get our environment set up. First, we need John’s graphics library. Second, we need to place it in the same folder as our code because it is not part of the python standard libraries. You can download the graphics.py file from John’s site here: http://mcsp.wartburg.edu/zelle/python/graphics.py. Once you have the library place it in the same folder as your project file. It is important that you place the “graphics.py” file in the same folder as your code file. Otherwise python may not be able to locate it. Also note that you will tkinter (python-tk) installed on your machine to use John’s library.


While Completing this series of blog posts, I had a minor issue. The issue turned out to be that I misunderstood the way autoflush=false worked, we’ll cover that much later in this series. I wrote John and he set me straight on the inner workings of the update() method, (also covered later). In his reply he informed me that his library can now be installed using pip. To install with pip run this command:

pip3 install –user http://bit.ly/csc161graphics

You can find more info here: http://www.pas.rochester.edu/~rsarkis/csc161/python/pip-graphics.html

Now create a folder for your projects. We will call this folder “graphics-zelle” and it will be placed in a “projects” folder.  Finally, we will create a sub-folder for each exercise we do. Create a file named ex-01.py and add the code below.

Ok, now let’s write some code!

Opening a window

To open a window that we can draw on requires two simple steps. First, import the graphics library file and second, instantiate a window object and assign it to a variable so we can access it later.

The first step is simple:

from graphics import *

Once we have imported the library we can instantiate the window by calling the GraphWin function and passing in the window title and it’s width and height. We’ll do this in function we’ll call main. Lastly, we need to call the main function to execute the program.


# Define a main function and instantiate the window object.
def main():
    window = GraphWin("Exercise-01", 640, 480)

main() # Call the main function 

Putting this all together we get:

"""
Prog:   ex-01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to open a window
        on the desktop.

Lic:    This code is placed in the public domain.

"""

# Creating a gui window requires only two steps
# Step 1: import the graphics.py library into 
# your local folder.

# Step 2: Instantiate a GraphWin object and assign
# it to a variable, passing the window title, and
# size parameters.

# Demo
from graphics import *

def main():
    win = GraphWin("Window Title", 640, 480)

    
main()

Now run ex-01.py. If you’re quick you’ll see a window pop open and immediately close. What’s going on here?

What’s going on is the program is doing exactly what was asked of it. It creates a window and then exits. So how do we stop this from happening? The easiest solution for the moment is get the program to wait for some input, until we close the window. We can add one line of code to ex-01.py to ask it to wait for a mouse click before exiting.


"""
Prog:  ex-01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to open a window
        on the desktop.

Lic:    This code is placed in the public domain.

"""

# Creating a gui window requires only two steps
# Step 1: import the graphics.py library into 
# your local folder.

# Step 2: Instantiate a GraphWin object and assign
# it to a variable, passing the window title, and
# size parameters.

# Demo
from graphics import *

def main():
    win = GraphWin("Window Title", 640, 480)
    win.getMouse()

main()

Now run the program. You should see the window open and stay open until you click the close button on the widow’s frame.  Clicking the window frame close button causes python to ask the OS and interpreter to clean up the window we created. This is a very poor practice. If we want the window destroyed, we should explicitly close it. We can do this by calling the close method on the window object. Modify your code as show below:


"""
Prog:   ex-01.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to open a window
        on the desktop.

Lic:    This code is placed in the public domain.

"""

# Creating a gui window requires only two steps
# Step 1: import the graphics.py library into 
# your local folder.

# Step 2: Instantiate a GraphWin object and assign
# it to a variable, passing the window title, and
# size parameters.

# Demo
from graphics import *

def main():
    win = GraphWin("Window Title", 640, 480)
    win.getMouse()
    win.close()    

main()

This is much better. Now we can close the window simply by clicking on it. Still not a perfect solution but it at least allows the GraphWin object to clean itself up.

Drawing Points

When I first started programming PC’s back in the 1980s, I learned that the first step to drawing anything on the screen was to draw a simple point. In a Compuserve chat with Andre’ LaMonthe (then a hot shot game developer), he told me if you can draw a pixel, you can draw anything! Take that to heart. It’s the most basic object you can put on the screen. A simple point of light and everything else is simply built up out of many of these points of light. So, that’s our first task is to draw a point. 

Create a file called ex-02.py and in it add the code below:

"""
Prog:   ex-02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw a point 
        in the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *

def main():
    win = GraphWin("Exercise-02, Points", 640, 480)
    # Our first point
    p = Point(320, 200)
    win.getMouse()
    win.close()
    
main()

Now if you run this code you may not see a point. It depends on you’re desktop’s default background color. On my desktop I have a darkula theme and my windows have a dark background. The default color in the graphics library is black and so in my window the point is very difficult to see.

To solve this we need to change the color of the point to something more easily seen. Before we can do that, we need to talk about how computer monitors display color.

Each pixel (picture element) on the screen is actually made up of fields. There is a field for each color channel, Red, Green, and Blue. From these three colors we can make almost any color. You may be saying wait? how can that be? These are not the primary colors I learned about in school. We’ll the colors you were taught were the primary colors are indeed one set of primary colors called the Sink or Subtractive primary colors.  When you add these colors (such as mixing paint or crayons) the color get darker. This is because the colors are reflecting light and adding another color means you’ll be reflecting more light off the surface. However, there is another set of primary colors known as the Source or Additive colors. These colors come from light sources so adding more colors means they get brighter. These are the primary colors used in computer monitors and light emitting devices.For the more curious readers checkout: https://stackoverflow.com/questions/6531536/why-rgb-and-not-ryb and http://en.wikipedia.org/wiki/Additive_color and http://en.wikipedia.org/wiki/Subtractive_color.

The graphics library we’re using has a special function for setting the values of these color channels. The color_rgb() method returns a color object that can be used in many of the library’s drawing methods. We simply need to pass in the values for the r, g, and b color channels. The values for these channels must be integers and be between a value of 0 and 255 inclusive. If we set a channel’s value to 0 it will turn that channel off. For example, if we pass (0,255,127) we are telling the system to add 0 red to the color, full green to the color, and half the available blue to the color. We can generate black by passing (0,0,0) and telling the system to add 0 red, 0 green and 0 blue to the color. We can also generate white by passing (255, 255, 255) to the system. And we can generate a gray scale of 256 levels from black to white by passing equal amounts of each color ex: (68, 68, 68).


"""
Prog:   ex-02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw a point 
        in the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *

def main():
    win = GraphWin("Exercise-02, Points", 640, 480)
    win.setBackground(color_rgb(0,0,0))

    # Our first point
    p = Point(320, 200) 
    p.setFill(color_rgb(255,255,255))
    p.draw(win)
                    
    win.getMouse()
    win.close()
    
main()

Looking at the code we can see we call setBackground() on the window object, passing it a color_rgb object set to produce a black color. This simply sets our windows background to black as you would expect. Next, we create a Point() passing in the x and y coordinates of where it should be drawn. In most computer graphics systems the upper left corner of the screen will be (0,0). The x coordinate grows larger as we travel across the screen from left to right, and the y coordinate grows larger as we travel from the top down. This is a Cartesian coordinate system using only the fourth quadrant and taking the absolute value of the y axis. If that sounds confusing, see: https://www.mathsisfun.com/data/cartesian-coordinates.html or simply google Cartesian Coordinate System.

Next, we see that call the setFill() method on our point and pass in a color_rgb object with three equal values for red, green, and blue. So this will set the point’s color to white. Lastly, we call the draw method on the point so that it is displayed on the screen.

Run the code and you should see a small white dot in the middle of a window with a black background. It’s really that simple.

Now that we can draw a point in the window, let’s have some fun! First, let’s put what we’ve learned about setting colors and plotting points to work. We’ll create a little program to fill our screen with random points of random colors. Enter the code below:


"""
Prog:   ex-02.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw a point 
        in the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def main():
    width = 640
    height = 480

    win = GraphWin("Exercise-02, Points", width, height)
    win.setBackground(color_rgb(0,0,0))

    # Draw 10,000 points in the window
    # Each of a random color and at a
    # random position.
    for i in range(10000):
        # Get random position
        x = randint(0, width)
        y = randint(0, height)
        p = Point(x, y)
    
        # Get random color
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        p.setFill(color_rgb(r, g, b))
        p.draw(win)
                    
    win.getMouse()
    win.close()
    

main()

Inspecting the code above the first thing we see is that we’ve imported the random library’s randint() method. We’ll use this method to generate random integer values for our point’s location and it’s color values.

Next, we see we created variables width and height to hold the width and height of the window. We then pass these variables to the GraphWin() method. Then we create a simple loop that iterates from 0 to 9,9999, for a total of 10,000 values. Within this loop we generate a point with random x and y coordinates and then generate a random color and set that color for the point’s color. Then we simply iterate and do it all again until we’ve filled the window with 10,000 random points of random colors.

Drawing Lines With Points

Ok, that was fun. But, we want to be able to draw more than just points. So, let’s try drawing a line. Now, we’ll use one of the most basic line drawing algorithms available. It’s the Bresenham line algorithm. This was the first line drawing algorithm I learned and perhaps the simplest. So it’s a great starting point for us. If you want to learn more about this algorithm check out Wikipedia’s article here: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.

But first, we'll start by drawing a simple horizontal line as this and the purely vertical lines are the simplest case.

We'll add a horzLine function to our code. To draw a horizontal line all we need to do is draw a series of points starting a (x1,y) and continuing to (x2,y). Notice the y coordinate remains constant for a horizontal line

Create a new file called ex-03.py and add the following code:


"""
Prog:   ex-03.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw horizontal
        lines at random positions and colors
        in the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def horzLine(x1, y1, x2, color, win): 
    for x in range(x1, x2):
        p = Point(x, y1)
        p.setFill(color)
        p.draw(win)


def main():

    width = 640
    height = 480

    win = GraphWin("Exercise-03, Lines", width, height)
    win.setBackground(color_rgb(0,0,0))

    # Draw 10,000 points in the window
    # each with a random color and at a
    # random position/
    for i in range(1000):
        # Get random position
        x1 = randint(0, width)
        x2 = randint(0, width)
        y = randint(0, height)
            
        # Set random color
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)

        # Draw our line using generated coordinates and color
        horzLine(x1, y, x2, color_rgb(r, g, b), win)
                      
    win.getMouse()
    win.close()
    
main()

As you can see I’ve added a function to create a horizontal line given the start and end points on the x axis and the y location on the y axis.  All we have to do is walk along the path drawing points until we reach the end of the line. Now it’s your turn. Create a function for drawing a vertical line and call it from the existing loop.

Hopefully, you were able to knock that code out quickly. If not, here’s a hint. All that is needed is to loop over the y coordinate just as we looped over the x coordinate in the horzLine function. Then in the main function’s loop we generate a single x coordinate and a y1 and y2 coordinate for our new line.  Here’s my solution:

"""
Prog:   ex-03.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw lines 
        at random positions and colors
        in the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

def vertLine(x, y1, y2, color, win):
    for y in range(y1, y2):
        p = Point(x,y)
        p.setFill(color)
        p.draw(win)

def horzLine(x1, y1, x2, color, win):  
    for x in range(x1, x2):
        p = Point(x, y1)
        p.setFill(color)
        p.draw(win)

def main():

    width = 640
    height = 480

    win = GraphWin("Exercise-03, Lines", width, height)
    win.setBackground(color_rgb(0,0,0))

    # Draw 10,000 points in the window
    # each with a random color and at a
    # random position/
    for i in range(1000):
        # Get random position
        x = randint(0, width)
        y1 = randint(0, height)
        y2 = randint(0, height)
        
            
        # Set random color
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)

        # Draw our line using generated coordinates and color
        vertLine(x, y1, y2, color_rgb(r, g, b), win)
                    
    win.getMouse()
    win.close()
    
main()

If you run this code you’ll see random vertical lines drawn at random locations and in random colors.  I think that was pretty straight forward.

Now what about lines that are not horizontal or vertical? How do we draw them? Well we will use a version of Bresenham line algorithm to handle this. This algorithm is designed to handle lines at arbitrary angles.  Therefore it can draw any line in our 2d window. The basic idea of the algorithm is to compute a step and direction such that the point positions fall on integer (x,y) coordinates. This is important as some of the pixels would typically fall partially on two separate (x,y) coordinates. That’s a problem because we can’t draw a partial point. So the algorithm calculates how far we should move in one direction before we take a step in the other direction, so that all our points end up on integer coordinates. For example, how far we should move horizontally before moving vertically. You can find a simple discussion of the algorithm here: https://www.tutorialspoint.com/computer_graphics/line_generation_algorithm.htm and a more in depth discussion at: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm. For those who prefer videos, watch this youtube video for more details: https://www.youtube.com/watch?v=zytBpLlSHms. If you want some truly awesome and detailed text on the subject of computer graphics check out the Graphic Gen series. While they may be older books the information in them is still very relevant.

Ok, so let’s see how we can implement a simple version of this:


"""
Prog:   ex-03.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw lines 
        at random positions and colors
        in the window using points.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint

# Bresenham's line drawing algorithm
# to handle lines of any orientation
def line(x1, y1, x2, y2, color, win):
    # Calculate dx, sx
    dx = x2 -x1
    if dx < 0:
        sx = -1
    else:
        sx = 1
    # calculate dy, sy
    dy = y2 - y1
    if dy < 0:
        sy = -1
    else: 
        sy = 1
  
    if abs(dx) > abs(dy):
        slope = dy/dx
        pitch = y1 - slope * x1
        while x1 != x2:
            y = slope * x1 + pitch
            p = Point(x1,y)
            p.setFill(color) 
            p.draw(win)
            x1 += sx
    else:
        slope = dx/dy
        pitch = x1 - slope * y1
        while y1 != y2:
            x = slope = slope * y1 + pitch
            p = Point(x,y1) 
            p.setFill(color) 
            p.draw(win)
            y1 += sy  


def vertLine(x, y1, y2, color, win):
    for y in range(y1, y2):
        p = Point(x,y)
        p.setFill(color)
        p.draw(win)


def horzLine(x1, y1, x2, color, win):  
    for x in range(x1, x2):
        p = Point(x, y1)
        p.setFill(color)
        p.draw(win)


def main():

    width = 640
    height = 480
    color = None

    win = GraphWin("Exercise-03, Lines", width, height)
    win.setBackground(color_rgb(0,0,0))

    # Draw 10,000 points in the window
    # each with a random color and at a
    # random position/
    for i in range(100):
        # Get random position
        x1 = randint(0, width)
        x2 = randint(0, width)
        y1 = randint(0, height)
        y2 = randint(0, height)
            
        # Set random color
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)

        # Draw our line using generated coordinates and color
        line(x1, y1, x2, y2, color_rgb(r, g, b), win)
    
    color = color_rgb(255,255,228)

    line(width/2, 0, 0, height/2, color, win)
    line(0, height/2, width/2, height, color, win)
    line(width/2, height, width, height/2, color, win)
    line(width, height/2, width/2, 0, color, win)
                    
    win.getMouse()
    win.close()
    
main()

Now John new that lines would be a desired feature of his library so he included a line drawing function. So why did we draw lines using points then? Well, so you would have an idea of just what the graphics library is doing for you.

Drawing Lines with the Library

Using the Line method of the graphics library is pretty simple. First, you create a Line object and assign it to a variable.

p1 = Point(0,0)
p2 = Point(640,480)
myLine = Line(p1, p2)
myLine.setFill(color_rgb(0,255,255))
myLine.draw(win)

Using line is really no different than using point. There is one aspect we didn’t touch on. That is The width of a line or point. You can set the width of a line or point with:

p1.setWidth(5)
myLine.setWidth(5)

The width value is given in terms of pixels.

Try using the library Line method in our ex-03.py random line program.

Calculating the Distance Between two Points

OK, create a new file called ex-04.py. In this section we are going to calculate the distance between two points. Now if you’ve had geometry in school you may recall that we can calculate this distance with the formula sqrt((x2 – x1)^2 + (y2 – y1)^2). So let’s create a new function called dist() that given two points will return the distance between them.

We don’t really need the graphics library to develop this function however, we can use it to draw the a line between the two points. So I’ve included it in the code in ex-04.py. We’ve developed this function as it is missing (IMHO) from the library (perhaps John left it out so his student had to code it manually?). However, it is easy to develop. enter the code below in to the ex-04.py file and run it. You should she a line from the upper left corner of the screen to the lower right corner. The window size here has been hard coded to (400 x 400). This makes it easy to calculate the length of the line using a calculator so we can confirm our function is working correctly.

#!/usr/bin/env python3

"""
Prog:   ex-04.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to find the 
        distance between two points in the 
        window.

Lic:    This code is placed in the public domain.

"""
from graphics import *
from math import *

# Given (x1,y1) and (x2,y2)
# determine the distance 
# between the two points.
def dist(x1, y1, x2, y2):
    return sqrt(((x2-x1)**2) + ((y2 - y1)**2))

def main():
    win = GraphWin("Distance", 400, 400)
    win.setBackground(color_rgb(0,0,0))

    x1 = 0
    y1 = 0

    x2 = 400
    y2 = 400

    p1 = Point(x1, y1)
    p2 = Point(x2, y2)
    myLine = Line(p1, p2)
    myLine.setFill(color_rgb(188, 128, 176))
    myLine.draw(win)

    d = dist(x1, y1, x2, y2)
    
    print("Distance is: " + str(d))

    win.getMouse()
    win.close()

main()

If you run this code, you should see the length print in the terminal window, not the gui. The length should be 565.685…  You can check that with a calculator.

Ok, that brings up another issue. We’ve drawn points and line but what about text? We’ll get to the soon. I promise. But first let’s touch on circles.

Drawing Circles

We’ll begin by plotting our own circles and then touch on the Circle commands in the graphics library. Go ahead and create a new file ex-05.py and leave it empty for now.

Bresenham who we discussed earlier when talking about line drawing actually has an algorithm for circles. There on many circle drawing algorithms each with it’s own advantages and trade offs. We’ll use one of the simpler solutions for circle drawing. We’ll use plot the x,y coordinates of each point along the circumference using sin and cos from the python math library. Note that this is perhaps the poorest performing circle algorithm but it is simple to implement and easy to understand for most middle and high school math students.

The formula for each point on our circles circumference is: x = cos(i)*r + cx; y = sin(i)*r + cy; Where x and y are point coordinates, i is the angle in radians, cx and cy are the circle’s center coordinate, and r is the circle’s radius.  What we want to do is walk through 360 degrees or 2Pi radians. We could convert the degrees to radians but in practice, it works best to simply plot a minimum of 58 points. Why 58? Well 360 degrees / 6.28 ~= 58. So by plotting 58 points we walk all the way around our circle. We’ll see shortly that it’s not quite as easy as this but it’s a start.

Open your ex-05.py file and enter the code show below:


#!/usr/bin/env python3

"""
Prog:   ex-04.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw circles 
        using points.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from math import *

# Given the center x, center y
# and radius, draw a circle 
# using points.
def circle(cx, cy, r, color, win):
    # loop over 360 degree arc plotting pixels
    for i in range(0,58):
        x = cos(i)*r + cx;
        y = sin(i)*r + cy
        
        p = Point(x,y)
        p.setFill(color)
        p.draw(win)

def main():
    win = GraphWin("Circles", 400, 400)
    win.setBackground(color_rgb(0,0,0))

    circle(200, 200, 50, color_rgb(255, 128, 200), win)

    win.getMouse()
    win.close()

main()

Now if you run this code you’ll see our circle has many gaps in it’s circumference. This is because our point resolution isn’t high enough for the diameter of the circle we are drawing. So, we have two options:

  1. Shrink the size of our circle until the points meet.
  2. Draw many more points on the circumference of the circle.

Option 1 doesn’t work very well as we may need a larger circle. Option 2 reduces performance but allows us to grow the circle larger without producing gaps. So, we will take this route for now. Simply dump up the number of points produce in the loop from 58 to say 360. Now try the code.

That actually looks pretty good right? Ok, next try increasing the radius from 50 to 100 or 150 in the call to circle(). Oops! With a larger circle our missing points problem comes right back. Using this method to draw circles requires a balance between speed, size, and resolution. We can increase the circle size and resolution at the cost of speed, or reduce the resolution and increase speed at the cost of circle diameter. You’ll find much of engineering, not just Computer Science, requires constant trade-offs. Getting these right for every circumstance is as much an art as mathematics.

Above I only gave you two options for dealing with the gaps in the circles circumference. There are other methods that can be used. One such method is to use line segments to connect the points along the circumference. With this method you would simply place all your points in a list, sort them radially, and then use that list to draw line segments between them. At the end you’ll need to connect the last point to the first with another line segment. This sounds like a great exercise for the reader so I’ll leave it to you.

Drawing Circles with the Circle Method

OK, so you’ve seen that drawing lines and circles isn’t as straight forward as one might think. So let’s see how well the graphics library does on circles. We’ll add a few lines of code and compare out circles.  Edit ex-05.py to read as follows:


#!/usr/bin/env python3

"""
Prog:   ex-04.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw circles.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from math import *

# Given the center x, center y
# and radius, draw a circle 
# using points.
def circle(cx, cy, r, color, win):  
    for i in range(0,360):
        x = cos(i)*r + cx;
        y = sin(i)*r + cy

        p = Point(x,y)
        p.setFill(color)
        p.draw(win)

def main():
    win = GraphWin("Circles", 400, 400)
    win.setBackground(color_rgb(0,0,0))

    circle(200, 200, 150, color_rgb(255, 128, 200), win)

    # Draw circle from library
    p1 = Point(200,200)
    c = Circle(p1, 200)
    c.setOutline(color_rgb(220, 128, 164))
    c.draw(win)

    win.getMouse()
    win.close()

main()

Here you can see that using the Circle method from the library follows the same pattern as the Point and Line method. First, we instantiate an object and assign it to a variable, passing coordinates as a Point object. Then we call setOutline to passing a color_rgb object to set the outline color. We could also call setFill and pass a color_rgb object but that would hide the circle we drew ourselves. So if you decide to try setFill with Circle, make the calls to draw the Circle object before calling our own circle function. 

Now if you run this code, you’ll see that the library draws a much nicer circle than we do. This tutorial is more about using and appreciating the graphics library than about all the graphics algorithms. So I’ll leave it to the reader to explore the web in search of better 2D circle drawing algorithms.

Drawing Text in the Window

OK, as promised, we will now cover drawing text. Drawing text is much more complex than circles or lines so we wont try this ourselves. However, I encourage you to give it a try and see what you can accomplish. Playing around with code and ideas will give you a deeper understanding of programming in general, and besides, its a lot of fun!

The Text() method of the graphics.py library instantiates a text object that is centered around it’s anchor point. The anchor point is an (x,y) coordinate you pass to the method to position it in the window. You also need to pass the text string to be displayed.   You can also changed the text on the Text object by calling setText and retreieve the text contained in the object by calling getText. You might think that setting the text color would require a call to setFill or setOutline. But, you would be wrong. The text object requires you call setTextColor and pass a color_rgb object. Other notable text methods include setFace which sets the font family. setAnchor to set the text position (remember the text will be centered around this position. setSize which takes an integer font-point size (not to be confused with Points) between 5 and 36.  setStyle allows you to pass in strings of “bold”, “italic”, “italic bold” and “normal”. These do eactly what you expect.

So let’s see some Text in action. Create a file named ex-06.py and add the code shown below:


#!/usr/bin/env python3

"""
Prog:   ex-04.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to draw text in
        the window.

Lic:    This code is placed in the public domain.

"""

from graphics import *
from random import randint
from math import *

width = 640
height = 480

def main():
    win = GraphWin("Circles", width, height)
    win.setBackground(color_rgb(0,0,0))

    messages = {"John Zelle", "Allen Turning",
                "Tommy Flowers", "Max Newman",
                "Gordon Wlchman", "John Atanasoff",
                "Tony Sale", "William Tutte",
                "Konrad Zuse", "Howard Aiken",
                "Charles Babbage", "Ada Lovelace",
                "Jack Kilby", "Robert Noyce",
                "Grace Hopper", "Ted Hoff",
                "Doug Engelbart", "Paul Otlet"
                "George Stibitz", "Clifford Berry"}
    fonts = ["helvetica","courier", "times roman", "arial"]
    styles = ["normal", "bold", "italic", "bold italic"]

    for msg in messages:
        # randomly pick a display point
        x = randint(100, width-100)
        y = randint(20, height-20)
        # randomly pick a text color
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)

        p = Point(x, y)
        t = Text(p, msg)
        t.setTextColor(color_rgb(r, g, b))

        size = randint(10, 20)
        t.setSize(size)
        # Randomly set the font face and style
        s = randint(0,3)
        f = randint(0, 3)
        style = styles[s]
        print("style: " + style)
        t.setStyle(style)
        font = fonts[f]
        t.setFace(font)

        # draw the text to the window
        t.draw(win)
     
    win.getMouse()
    win.close()

main()

Run the code and you should see a bunch of names randomly placed on the screen at random locations, with random styles, sizes, colors, and font families.  This is just a simple demo. BTW, if these names mean nothing to you I recommend that if you’re serious about computer science you look these people up and read some of their history and any papers they have written.

OK, we can now place text on the screen. But how can we get text from the user? For this, graphics.py contains an object called Entry. Entry objects are little text boxes you enter text into. The Entry object is just an extension of the Text object we just saw. However, it is designed to allow the programmer to retrieve text from the user. Where the Text object is designed to display text to the user. The Entry object has the same set of methods as the Text object including all the font and style methods. It centers itself on the anchor as does the Text object. The only real difference is that the user can type into it.

So, how do we use the Entry to get text from the user? It’s really very simple. Set up the Entry object the same way you do Text and then call the getText() method to read the text out. Note that reading the text is nondestructive. If you need to clear the Entry after reading text you’ll need to call setText() on the object with an empty string. 

I’ve put together a simple example but before you can use it, you need to create a file called button.py and place it in the same folder as your other project code. This file provides a button widget built up out of  Rectangle and Text objects. If you have johns version you can use it. However, modified my version show below has added features. So create a button.py file and place the button code below in it.


"""
Prog:   button.py

Auth:   John Zelle

Mods:   Randall Morgan

Desc:   This is a modified version of John Zelle's
        Button class. Randall Morgan added additional
        features to allow the button outline, background,
        font face, and text style to be change. Also added
        is a method to force a redraw on the button.

Lic:    This code released under GPL to remain 
        compliant with John's original license.

"""

from graphics import *

class Button:

    """A button is a labeled rectangle in a window.
    It is activated or deactivated with the activate()
    and deactivate() methods. The clicked(p) method
    returns true if the button is active and p is inside it."""

    def __init__(self, win, center, width, height, label):
        """ Creates a rectangular button, eg:
        qb = Button(myWin, centerPoint, width, height, 'Quit') """ 

        self.win = win
        w,h = width/2.0, height/2.0
        x,y = center.getX(), center.getY()
        self.xmax, self.xmin = x+w, x-w
        self.ymax, self.ymin = y+h, y-h
        p1 = Point(self.xmin, self.ymin)
        p2 = Point(self.xmax, self.ymax)

        self.text_color = color_rgb(0,0,0)
        self.text_family = "helvetica"
        self.text_style = "bold"
        self.fill = color_rgb(200,200,225) # fill for btn background
        self.border_color = color_rgb(255,255,255)
        
        self.rect = Rectangle(p1,p2)
        self.rect.setOutline(self.border_color)
        self.rect.setFill(self.fill) 
        self.rect.setOutline(self.border_color)
        self.rect.draw(win)

        self.label = Text(center, label)
        self.label.setTextColor(self.text_color)
        self.label.setFace(self.text_family)
        self.label.setStyle(self.text_style)
        self.label.draw(win)
        self.deactivate()
        
    def clicked(self, p):
        "Returns true if button active and p is inside"
        return (self.active and
                self.xmin <= p.getX() <= self.xmax and
                self.ymin <= p.getY() <= self.ymax)

    def getLabel(self):
        "Returns the label string of this button."
        return self.label.getText()

    def activate(self):
        "Sets this button to 'active'."
        self.rect.setFill(self.fill)
        self.rect.setWidth(2)
        self.active = True

    def deactivate(self):
        "Sets this button to 'inactive'."
        self.label.setFill(self.text_color)
        self.rect.setWidth(1)
        self.active = False

    def setFill(self, color):
        "Sets the fill color of the button"
        self.fill = color
        self.rect.setFill(self.fill)
        self.rect.setOutline(self.border_color)

    def setTextColor(self, color):
        self.text_color = color
        self.label.setTextColor(color)

    def setTextFace(family):
        self.text_family = family
        self.label.setTextFace(family)

    def setTextSyle(style):
        self.text_style = style
        self.label.setTextSyle(self.text_style)

    def setBorderColor(self, color):
        self.border_color = color
        self.rect.setOutline(self.border_color)

    def draw(self, win):
        self.rect.undraw()
        self.label.undraw()
        self.rect.draw(self.win)
        self.label.draw(self.win)

I recommend you read through the button code. It’s pretty straight forward and you may find modifications you yourself would like to make.

Time to get on with our text input demo. We will set aside any discussion of the button code and concentrate on the text Entry object’s use.Create a file named ex-07.py and add the code shown next to it. Then run the code.


#!/usr/bin/env python3

"""
Prog:   ex-07.py

Auth:   R. Morgan

Desc:   Demonstrate how to use John Zelle's
        graphics.py library to get text entered 
        by the user into an Entry object.

Lic:    This code is placed in the public domain.

"""
from graphics import *
from button import *
from random import randint
from math import *

width = 400
height = 400

words = ["Tom Collins", "John Smith", "Alan Turing", "Mike Jones"]

def show_list(offset, words, win):
    y = 40
    for word in words:
        p = Point(offset, y)
        tbox = Text(p, word)
        tbox.setTextColor(color_rgb(255,255,255))
        tbox.draw(win)
        print("show: " + word)
        y += 20

def main():
    global words

    win = GraphWin("Word Sort", width, height)
    win.setBackground(color_rgb(0,0,0))

    p = Point(100, 40)
    box = Entry(p, 20)
    box.setText("Enter a word to sort")
    box.draw(win)

    p1 = Point(100, 80)
                #win, center, width, height, label
    btn = Button(win, p1, 100, 30, "Add to list")
    btn.setFill(color_rgb(200,200,225))
    btn.setBorderColor(color_rgb(255,255,255))
    btn.setTextColor(color_rgb(0,0,0))
    btn.activate()
    btn.draw(win)
   
    show_list(300, words, win)

    while True:
        pos = win.getMouse()
        if btn.clicked(pos):
            # get the text from the 
            # Entry and put it in the list.
            # then redraw the list
            text = box.getText()
            box.setText("")
            words.append(text)
            show_list(300, words, win)

    # Close app    
    win.close()

main()

The ex-07.py app opens a window and displays a Entry fields and a few names in a list on the right. Click in the Entry field and delete the current text. Next, type a name and click the button. The name will be added to the list.

The important thing to realize here is that the call to getText() on the Entry object simply returns immediately with what ever text is in the text field. This means we need to monitor some action, such as clicking a button, to indicate we need to read the text from the Entry object. The infinite while loop handles this for us. Once the loop is entered, the system will remain in the loop until the application is closed. So the win.close() method will never be called. This will cause an error message to be printed on exit. A better approach would have been to add an Exit button so the user could click it to exit cleanly. This would only require another if statement to handle the click on the Exit button. 

Alright, I think this will do for this post. I’ll continue this post soon and add more drawing functions and more information on the library. Their might even be a simple game soon.

Hope you enjoyed the post. If you find it helpful leave a comment and let me know.


Laser Communications With Arduino Developed MPA Workshop Series

by SysOps 0 Comments

I’ve been giving a series of workshops for several months now. These workshops have been focused on understanding the basics of the underlying computer hardware. But much of that material can get rather technical and so we needed some hardware projects that didn’t involve building logic gates from transistors or designing CPU hardware in Logisim or Digital. We needed a fun project to break up the more technical material we’ve been covering. So, I discussed some ideas with the host and we came up with a simple Laser Communications System using the Arduino Nano. The project is simple enough to complete in  an evening. Exciting enough to gain interest. After all, what geek wouldn’t want to play with a laser? So I spend a bit of time developing this simple project and thought I’d share it here.

Before we begin, we’ll need a few supplies.  Tools, components and a computing device capable of running the Arduino IDE. Any laptop, table, or desktop machine should do. Some larger cellphones may even work. Check and see if you can install the Arduino IDE on your phone first, if you are intending to use your phone.

Since this is a communications system, you’ll need two systems so you have a transmitter and receiver. Each system actually functions as both so two way communications is possible. But you still need something to communicate with. The parts list is for a single unit. So, if you are building this by yourself you’ll need to build two. So let’s see what we’ll need:

  • A Solder-less Breadboard

An Arduino board with a Usb connector and headers. We used the Nano

  • Assorted jumper wires.
  • A 2n2222P NPN Transistor. T0-92 case preferred.
  • Two 39K or 47K through-hole, axial-lead 1/4 watt resistors.
  • A DTOL 10 X Mini Laser Dot Diode Module Head WL Red 650nm, 5v, 5mW. Can be found on Amazon and eBay. Part number: DTOL-SHUS1517
  • A Small Signal Photo Transistor in T-1 5mm Case. Must be sensitive to lights at 650nm. I used some clear 400nm to 1000nm Photo transistors from Amazon. They worked well.
  • A 220 or 330 ohm 1/4 watt through-hole, axial-lead resistor.

So, first let’s look at the schematic for the Laser. Here we can see a simple NPN2N2222P transistor being used in switching mode. The base of the transistor is connected to the Arduino board’s Digital Pin 2 (D2) through a 39K ohm resistor. The resistor limits the current to the base and ensures proper operation of the transistor. The current applied to the base of the transistor is multiplied by the transistor and the multiple is passed through the collector – emitter circuit. A typical 2N2222P should have a gain (multiplying effect) or around 200 or more. So what ever current we place on the base will be multiplied by ~200 and that amount of current will pass through the collector – emitter circuit. This all actually over simplified but it gives a basic understanding of the circuit. By using the transistor we can control much more current than the Arduino board’s output pins are capa

Laser Control Circuit

ble of supplying. If we were to connect the Laser to the Arduino directly we could cause damage to the Arduino. Since it’s possible that the transistor could pass more current than our Laser diode is capable of using, we could also cause damage to the laser if we did not limit the current flow through the laser with R2 a 330 Ohm resistor. The value of this resistor may be as low as 100 ohms or as high as 400 ohms. The lower the rating the more current will flow and the brighter the laser. However, the risk of damaging the laser or the surrounding circuit also increases as this number is lowered. I recommend you stick with values between 180 ohms and 330 ohms.

 

Let’s see how this sub-circuit looks on the breadboard. Note that we don’t yet have R4 connected to pin D2 of the Nano. We need the Nano for power at this time but we don’t want to connect our control circuit until we know it works as expected.

Wire up the board as shown. I recommend you use the same color wires if possible. Always use RED for positive power and BLACK for 0-Volts or ground. Make sure you use the 5-Volt pin on the Arduino board and not the 3.3-volt pin.

Laser Control Circuit

When placing the Laser on the board the long leg (Anode) should be placed so it connects to R2 the 330 ohm resistor. The short leg (on the side with the flat spot on the rim) is called the Cathode. The cathode should connect to the collector of the transistor.

When placing the transistor the pins out are as shown for most 2N2222 transistors. If your’s has a part number that begins with a “P” then the collector and emitter may be switched. But most will be exactly as shown. This should put the flat side of the transistor facing you if you wire the board as I did.

Transistor Pin Out

Once you have the transistor placed and the board wired as show, double check everything. If there is someone around who can double check your work for you, that is always helpful. Be particularly cautious of the connections to the Arduino board.

Now, first we will be a manual test of our sub-circuit to ensure it works as expected. Apply a jumper wire from the open (non connected) end of R4 to ground. Now plug the Arduino board into a USB port on your computer. The Laser should be off at this point.

Next, move the jumper wire to make a connection from the open end of R4 to the positive rail (5-volts). Now the Laser should turn on. If this works as expected you’re ready to write some test code. If not, then you need to double check everything. Make sure you have +5 volts to the positive (RED rail of the breadboard and 0-volts on the blue or black rail). Also, check that your Laser is placed with the long led connected to R2 and the other end of R2 is connected to +5 volts. Next, make sure your Transistor is placed properly and try turning it around if the part number begins with a “P”.  Have someone else check the circuit for you. A new set of eyes often sees more than the tired set that’s been looking at the same thing for hours.

Above showing R4 jumper to GND. Laser should be off. Arduino must be powered.

Above showing R4 jumper to Vcc +5 Volts. Laser should be on. Arduino must be powered.

 

Test The Laser Driver

OK, now that we have the laser working manually, it’s time to write some code. If you don’t already have Arduino IDE installed, go on over to www.arduino.cc go to the Software menu and select download. Follow their instructions for installing the IDE and getting your Arduino board up and running with the Blink example provided in the IDE. Once you have that done, or if you already have your IDE installed, move on to the next step.

Our first step will be to connect the open end of R4 to the D2 pin on the Arduino board. Next, open a new sketch in the Arduino IDE and enter the following code exactly as shown below:

Listing #1


/*
   LASER Blink
   BY: Randall Morgan

   Turns the LASER on for one second, then off for one second, repeatedly.

   We use D2 to drive the base of the switching transistor.
   D3 will be used later to read the Phototransistor.
   
   This example code is in the public domain.
   www.randallmorgan.me
   www.eastern-montana-tech.com

*/

int LASER_OUT = 2; // Laser control pin
int DELAY_MS = 1000; // 2000 ms (2 Seconds) cycle period

// the setup function runs once when you press reset or power the board
void setup() {
   // initialize digital pin 2 as an output.
   pinMode(LASER_OUT, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
   // Loop turning LASER on and off
   digitalWrite(LASER_OUT, HIGH); // turn the LED on (HIGH is the voltage level)
   delay(DELAY_MS); // wait for a second
   digitalWrite(LASER_OUT, LOW); // turn the LED off by making the voltage LOW
   delay(DELAY_MS); // wait for a second
}

Load and run this code by pressing the right facing triangle in the IDE. You should see the Laser Diode turning on and off. This should continue until you kill the power to the board.

 

Receiving Light Signals

OK, so now we have a way to send light pulses. What we need now is a way to receive light pulses. To do this we will use the Photo-transistor.  This is a transistor who’s base is exposed as a light sensitive element under the plastic package. Actually, all transistors are sensitive to light but a Photo-transistor is manufactured to take advantage of this phenomenon where other types of transistors are designed to minimize this phenomenon. Photo-transistors aren’t the only light sensitive devices we could use. Photo resistors are slower but work very well for sensing ambient light. Photo diodes work almost like Photo-transistors and can switch very fast for high speed communications.

In our project we chose the photo transistor as they are easy to work with and readily available. There is a three legged photo-transistor. The third leg is an exposed base. Basically, you can use the exposed base to adjust the sensitivity of the device. That’s a real nice feature but it complicates the design. So we chose not to use this type of photo transistor. If you purchased one of these it may work fine with the base left floating (unconnected). Sp give it a try before going out and purchasing the two legged type.

Complete Schematic

Looking at the schematic we can see that the photo transistor has it’s emitter tied to ground. The collector is connected to Vcc +5 volts through a 39K ohm resistor. This arrangement is known as a PULL-UP configuration. When the photo transistor is in the dark (or at least not receiving light on it’s base element), it is turned off and no current can flow through the transistor. This allows R1 to pull the input pin D3 on the Arduino to 5 volts. This creates a logic hi on the input. When the transistor receives light it turns on and pulls the collector down to near ground. This applies a logic lo or zero on pin D3. In this way we can sense the incoming light pulses.   Please ignore the dashed line. Fritzing kept replacing it after I removed or rerouted it. Very frustrating…

Photo Transistor Circuit Added

 

Now lets write some code to test this circuit. If you’re wondering why I’m not doing a manual test first, it’s because the circuit is very simple and about the only two things that may go wrong are that perhaps you put the photo transistor in backwards or you connect it to the wrong pin on the Arduino. If you have a multi-meter you can manually test the circuit by measuring the voltage on the collector of the photo-transistor. Cut a short piece of soda straw and place it over the photo-transistor. Then place the Laser Diode in the other end. SO that the laser shines on the end of the photo transistor. The voltage should drop on the collector to somewhere around 0.1-0.2 volts of less. If it does, you’re good to go. If it doesn’t, try turning the transistor around.

We’re going to write a little test code before we go all out and start sending messages to Mars and beyond. We use a little program to flash the laser on and off, one second in each state. During the middle of the state we’ll read the photo transistor on the digital input pin 3.

Now we need a way to communicate back to user just what the value read from the input is. We could setup an led and turn it on if the input is one or, we could just use the serial port to send the value back to the serial monitor in the Arduino IDE. I think this second approach is best and requires no additional hardware. So let’s see what this looks like:



/*
  LightReader
  By: Randall Morgan

  This test circuit simply toggles the laser connected
  to digital pin D3 on and off at a rate of 1/2 Hertz.
  After toggling the laser's state it then reads the
  input from the photo transistor on digital pin 2.

  To run this program you'll need an Arduino board
  with a laser diode connected via an npn switching
  transistor to digital pin 3.

  In addition you'll need a photo transistor connected
  to a resistor in a pull-down arrangment and the
  junction of the pull-up resistor and the photo
  transistor collector connected to digital pin 2 on
  the Arduino board.

  See the article at: http://www.randallmorgan.me/blog/
  Search for Arduino Laser Communications

  This code is placed in the public domain
*/

int LASER_OUT = 5; // Laser control pin
int PHOTOTRANS_IN = 3; // Photo transitor sense pin
int DELAY_MS = 1000; // 2000 ms (2 Seconds) cycle period
int val = 0; // input value
char buf[100];

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LASER_OUT, OUTPUT);
  pinMode(PHOTOTRANS_IN, INPUT);

  // Set up serial port
  Serial.begin(9600);
  // Wait for hardware serial to connect
  while(!Serial) {
    // wiating for connection....
  }
  Serial.print("Connected\n");
}

// the loop function runs over and over again forever
void loop() {
  // Turn on laser and read photo transistor
  digitalWrite(LASER_OUT, HIGH); // turn the LED on (HIGH is the voltage level)
  delay(DELAY_MS/2); // wait for a second
  val = digitalRead(PHOTOTRANS_IN); // read the photo transistor
  sprintf(buf, " Val: %d \n", val);
  Serial.print(buf);
  delay(DELAY_MS/2);

  // turn off laser and read photo transistor
  digitalWrite(LASER_OUT, LOW); // turn the LED off by making the voltage LOW
  delay(DELAY_MS/2);
  val = digitalRead(PHOTOTRANS_IN);
  sprintf(buf, " Val: %d \n", val);
  Serial.print(buf);
  delay(DELAY_MS/2);
}


Reviewing the code we can see two interesting additions. First, we call Serial.begin() in the setup() function and pass it the baud rate we want to use. Since the default baud rate for the serial monitor in the IDE is 9600, we use that. Next we wait for the serial library to get a connection to the host computer. The while loop performs this wait for us and exits when Serial returns true. This occurs when the serial library has a connection to the host.

In the function loop() we added calls to SerialWrite() and pass in a buffer containing a string of characters to send to the host. We cheated a little and initialized a character array of 100 characters. Ideally, you’d want to safe guard against buffer overflow here. If you don’t know what that is, do a little googling and read up on it.

Before we can send the value read from the digital input pin to the host we must convert it to a string representation. What we read is actually a number not a character. So, we pass the buffer and the value to the C function sprintf() for formatting and conversion. You can find good information on many C and C++ functions and libraries that can be used with Arduino googling the “Standard C/C++ libraries”.

OK, so now we can control our laser and sense when a laser is on or off when pointed at out photo transistor. The only piece we are lacking now is the code to allow us to communicate with another unit. To accomplish this, we only need to setup a software serial port. Software serial ports work similar to hardware serial ports except they are just routines running to read and send bits on the I/O pins.

The serial library requires the use of interrupts. An interrupt is a signal to the processor to stop what it is doing and service another routine. Since the SoftwareSerial routine needs to be called regualrly so it doesn’t miss a bit, it requires inputs that are interrupt capable. Both D2 and D3 are interrupt capable pins but they are not the only ones on the Arduino. So if you used other pins, you’ll need to move your control signals to pins with interrupt support. The SoftwareSerial library is shipped with Arduino IDE so there is no need to download and install another library.

So, let’s talk about what we need to accomplish with our code. First, we need to send data on the laser. We can do this by setting the laser control pin as the software serial libraries transmit pin. In order to receive data we simple need to set the photo transistor’s sense pin as the software serial libraries receive pin. Then we simple use the hardware serial port to send anything received on the software serial port to the host. Anything we receive from the host on the hardware port can be sent on by the software serial port. So let’s see how we can implement this:


/*
   LightReader
   By: Randall Morgan

   This test circuit simply toggles the laser connected
   to digital pin D3 on and off at a rate of 1/2 Hertz. 
   After toggling the laser's state it then reads the 
   input from the photo transistor on digital pin 2.

   To run this program you'll need an Arduino board
   with a laser  diode connected via an npn switching 
   transistor to digital pin 3.

   In addition you'll need a photo transistor connected 
   to a resistor in a pull-down arrangment and the 
   junction of the pull-up resistor and the photo 
   transistor collector connected to digital pin 2 on
   the Arduino board.

   See the article at: http://www.randallmorgan.me/blog/
   Search for Arduino Laser Communications 

   This code is placed in the public domain
*/

// Tell the IDE we want to use 
// the SoftwareSerial library
#include 

int LASER_OUT = 2;      // Laser control pin
int PHOTOTRANS_IN = 3;  // Photo transitor sense pin
int DELAY_MS = 1000;    // 2000 ms (2 Seconds) cycle period
int val = 0;            // input value
char buf[100];


SoftwareSerial laserSerial(PHOTOTRANS_IN, LASER_OUT);

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LASER_OUT, OUTPUT);
  pinMode(PHOTOTRANS_IN, INPUT);  

  // Set up serial port
  Serial.begin(9600);
  // Wait for hardware serial to connect
  while(!Serial) {
    // wiating for connection....
  }

  Serial.print("Connected\n");

  // Setup Software Serial
  laserSerial.begin(57600);

  // Now we start sending data to 
  // allow the users to line up the
  // lasers and photo transistors.
  // Once the user hits a key, we 
  // stop and begin transceiving 
  // data on the serial ports.
  while(!Serial.available()){ 
    laserSerial.write("Light is beautiful!...");  
  }
  
}


// the loop function runs over and over again forever
void loop() {
  // If the photo transistor has received 
  // and data, send it to the host
  if(laserSerial.available()) {
    Serial.write(laserSerial.read());
  }

  if(Serial.available()) {
    laserSerial.write(Serial.read());
  }
}


Now enter the code above into the Arduino IDE and upload it to your Arduino board. The system should begin sending the phrase “Light is beautiful!…” immediately. Use this phrase data to line up your laser with the second unit’s photo transistor. Once the alignment is complete, hit a key on the keyboard and the transmission should stop. The system is now in transceiver mode. Anything you type into the serial monitor will be sent to the second unit. Anything the second unit sends will be sent to the serial monitor for display.

Congratulations, you now have completed the project. But this is only the beginning. There are many improvements to both the code and the circuit that can be made. Take the circuit presented here and make changes to it. Share those changes with others and let’s see what you come up with.

Until next time, Happy Hacking!

Android Native Development with Java Getting to know Android

Mobile platforms such as Android and IPhone are becoming the de facto computing devices.  Many people have left their laptops and even tablets behind as the power of smart-phones has increased. Now applications that once ran on the desktop are running on smart-phones. Software development is changing. Every product must contend with mobile support whether it be a website or service, or a desktop application. Users now expect that their data will travel with them and they will have access to it from all the devices they own. For the software developer, that means supporting multiple devices, often supporting multiple code bases and feature sets. Android has been around now for about a decade and has grown to own most of the mobile market. IOS is a close second. There are tools out their that will help the mobile developer develop products for both platforms using a single code base. I’ve written about my favorite, Dart/Flutter in other articles. However, these tools often lack either the flexibility to accomplish certain tasks, or they simply do not support a critical function of the device. When this occurs it is often necessary to fall back on native development. Either writing code that your main application can call, or writing the complete application in the platforms native code. This means that even a cross platform developer using tools such as Flutter or Xamarin often need to maintain a considerable amount of platform specific knowledge. Simply put, knowing native development for a platform is important even if you use cross platform tool sets.

I recently began taking some Android course (thanks to a Google Scholarship, THANK YOU GOOGLE!!! ) and thought I would share my knowledge and experience here. These are not the first Android courses I have taken. But I still feel I am far from an expert in Android development. Things change so quickly and I often am playing catch up just trying to stay abreast of new technologies. Sharing my knowledge with others and teaching others helps me ground my own understanding of a topic. So here I will share what I know a little at a time.

Tools Needed

Throughout this series I will be using Android Studio. At the moment it is version 3.0.1. The latest API is 27 revision 1. Android Studio is free and can run on Linux, Mac, and Windows.  It includes an emulator for running your compiled code though, other emulators from 3rd parties are available. AndroidStudio can be downloaded from: https://developer.android.com/studio/index.html If you would like to follow along I suggest you download and install AndroidStudio. Read the “User Guide” particularly the sections starting from “Meet Android Studio” to “Build And Run Your App”. These sections will help you get things setup and walk you through setting up the emulator. I will assume a familiarity with the Java programming language throughout this series. If you have never used Java but have C++ or C# experience, you should be able to follow along just fine.

Step One:

Ok, now that you have Android Studio setup and working and have an virtual device working for testing. It’s time to start coding.

The base for most Android Apps is the Activity. An activity typically loads the User Interface (UI) and provides some interaction with the user. An activity goes through a number of stages during it’s life cycle.  For most applications the activity is the heart of the application. Most applications have one or more activities however, it is possible for an application to be written without using an activity. These are rare however and are more advanced than we will cover here. You can think of an activity as a user task.

Fig. 1.1 Android Activity Life Cycle. Courtesy, www.developer.android.com

Fig. 1.1 Android Activity Life Cycle. Courtesy, www.developer.android.com

As stated, activities have a life cycle. The android framework provides 6 core callback methods that allow you to interact with the activity life cycle. These are: onCreate(), onStart(), onResume(), onPause(), onStop(), and onRestart() as shown in figure 1.1.

Android activities get created and destroyed based on the user’s interaction and system resource requirements. If the user navigates aways from your application and opens another app that requires resources your app is using, your application will be destroyed and then rebuilt when the user navigates back to it. The same actions can occur when the user rotates the device your app is installed on. Rather than doing a complicated dance to redraw the layout in the new orientation, android simply destroys your app and then recreates it when the user returns to it. This may seem like a drastic measure to take however, android works hard to limit power consumption and extend battery life, and this approach aids that goal.

The onCreate() method is fired when the activity is first created. Android requires that if we use an activity we must override this method in our code. In the onCreate() method we perform the activity logic that should only be completed once in the activities life cycle. Here we can load the layout, set class variables, and retrieve data that may be needed for the activity.

The onCreate() method receives an parameter of savedInstanceState of type Bundle. A bundle is a collection of state variables and data saved from the last run of the application. If the application has never been ran before the bundle will be null and the android framework will create it for us on the first run.

So let’s try some code! Open Android Studio and create a new project. Call it “ActivityApp” and select “Empty Activity” for the application type. Android Studio will create some boiler plate code for you. It should look like this:

 [cc lang="java" tab_size="4" lines="20"]
package me.randallmorgan.activityapp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

}
[/cc]

The first line provides our Java package name. This name comes from the concatenation of the app name and the reverse of the domain name you enter when creating the project. This is a typical java standard for package naming. It ensures that no two packages are named exactly the same. So that when these packages are distributed, there is no confusion as to which package is which or who is responsible for the package. Android follows this java standard package naming convention.

Lines 3 and 4 include the needed framework libraries AppCompatActivity and Bundle. Line 6 is the beginning of our application. We declare a public class MainActivity that extends the AppCompatActivity class found in the android framework code. The public, protected, and private prefixes to our class and methods are used to annotate the visibility of the code. We wont worry about these just yet. But we’ll come back to them in a later post.

Next we Override the onCreate() method from the framework’s AppCompatActivity class and provide our own code to initialize our activity. First we call the onCreate() method of our parent calls AppCompatActivity. This ensures that the framework does the work it needs to do to initialize our activity, such as extracting the state information from the saveInstanceState bundle. Finally, we call setContentView() passing in the resource id of our layout. In this case, the default activity_main.xml layout.

The setContentView() method takes the layout file and turns it into an actual view on the screen. We use the resource layout file to store our UI design. We’ll talk about UI design next time. For now, let’s continue with the activity life cycle.

Logging

Android Studio provides a logging system that allows us to see debug messages. These messages can be grouped into various categories such as Error, Warning, Info, Assert, Debug, Verbose, and WTF (What a Terrible Failure). The WTF level is typically reserved for the framework developers and under normal development conditions shouldn’t be used by mobile developers. To use the logging methods we first need to import the “android:util.Log” library. Next we set a tag value. This tag can be any string value but is usually set to the current class name. Then to print out information to the debug output window (Logcat) we only need to call the Log.d() method. The Log.d method takes to strings values and an option Throwable. A throwable is an exception. Exceptions are a type of error handling that allows a program to catch recoverable errors and do some processing on non recoverable errors  (like logging them).

Log.d() is used for debugging messages. There are other Log methods. One for each type of error message we listed earlier. The ones you might use are Log.d(tag, msg), Log.e(tag, msg), log.i(tag, msg), Log.v(tag, msg), Log.a(tag, msg). The Log.wtf(tag, msg) should only be used if you understand what you’re doing. This is for catastrophic errors only!!! Again, it is best left to the Android framework developers.

So let’s see how logging works. Go back to our ActivityApp project. Open the main_activiy.java file. Edit the code as follows:

[cc lang="java" tab_size="2"]
package me.randallmorgan.activityapp;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
  final String mTag = "MainActivity";
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    
    super.onCreate(savedInstanceState);
    setContentView(R.layout.test_activity);
    
    Log.d(mTag, "onCreate called.");

  }
}
[/cc]

We’ve added line to declare a class wide string variable of “mTag” and set it’s value to “MainActivity” following standard convention. Next we added a line to the end of our onCreate() method to log our message passing Log.d() the variable mTag, and our message “onCreate called.”. If you run the app in your emulator and open the Logcat window you should be able to locate our message: … D/MainActivity: onCreate called. You may need to filter the output using the dropdown  to show only the messages in the Debug category.

Visualizing the Activity Life Cycle

Ok, now that we have debugging working, let’s try out some of the other functions that get called during the lifecycle of our activity. Edit your code to read as follows:

[cc lang="java" tab_size="2"]
package me.randallmorgan.activityapp;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
    final String mTag = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);
        Log.d(mTag, "onCreate called.");

    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(mTag, "onStart called.");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(mTag, "onResume called.");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(mTag, "onPause called.");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(mTag, "onStop called.");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(mTag, "onRestart called.");
    }
    
}
[/cc]

Now compile and run the app. Open the Logcat window and you should see the follow debug messages:

  • onCreate called.
  • onStart called.
  • onResume called.

Now rotate the view in the emulator. You’ll see a bunch of text scroll through the Logcat window. If you examine it you should see the following message:

  • onPause called.
  • onStop called.
  • onCreate called.
  • onStart called.
  • onResume called.

As you can see, our app was stopped by the Android framework and then recreated in the new orientation.

Ok, that’s it for today. Next time we’ll discuss layouts and the resource xml files. Then we’ll look at interacting with our layout.

Getting Started With Flutter – Part 1 Introducing Flutter

by SysOps 0 Comments

As I began to write this morning, I had Chrome open to the gitter.imflutter page. I’ve been a regular there for more than a month now.  Tismorning was unique. First a post was made that this past month had more traffic on the flutter site than any previous month. Second, that several people, myself included found that the flutter.io page was down do to exhausting it’s bandwidth for the month. Luckily, Google had it back up within a couple hours! This goes to show how popular flutter is becoming.

While flutter has been around a year or two, it is still has not been released for production. This past year flutter showed a great amount of growth as it is begining to come of age. This new level of maturity has cuased those like myself (who desire to build mobile apps for multiple platform from a single code base) take the leap. Yes, it is a risky venture. We all know how technologies tend to come and go and more often than not, end up as vapor ware. However, with flutter the risk is mitagatedsome by the fact that Google is both actively developing it and using it internally. They have been pushing it to their partners and have made flutter and it’s underlying dart language open sourced.

Now if you haven’t heard of flutter, you may be asking what it is. Flutter is a cross platform mobile development framework from Google. It is built on the Dart programming language. Dart is Google’s answer to some of the odities and limitations of Javascript. Be forewarned however, that Dart is different. Coming from a C background I find Dart has some peculiarities.  So it has taken me a bit to get comfortable with it. Still, I find that things don’t always work the way I think they should. This however, is just the learning curve that is more a result of my background inserting expectations on a language that is modeled on concepts foreign to the languages I have experienced in the past. This said, their are powerful features in Dart that make it well worth learning.

In this serries of articles I will walk you through some simple demos of flutter and it’s various features. At this point in flutter’s life, everyone is a noobe. The framework is still in it’s infancy so only the developers have a good grasp of all it’s workings. However, flutter is gaining steam and starting to get a foot hold in the App development market. People are already using it for major applications. Primarily because it reduces development time and does away with the need for multiple code bases to support multiple platforms.

So let’s get started building a simple app.

Prerequisites

Ok, to get started we need some tools and the Flutter platform. I’ll be using MS Code as an IDE mostly because it’s a simple IDE and has a Flutter plugin available. You can also use IBM IDEA, or Android Studio. You can infact use any Text Editor (Not a word processor) however, you’ll be missing the code highlighting and other features. There is plenty of info out there on the net on how to install this stuff. So rather than repeat it here, I’ll point you to the experts:

MS Code IDE: https://code.visualstudio.com/download

Flutter: https://flutter.io/setup/

Follow the instructions given on these sites to get your IDE and the Flutter framework setup. I’ll wait here until you’re done.

Ok, if you installed any of the three IDE’s I suggested, you can easily install the flutter plugin. In MS Code just search the plugins for flutter and install it.

Our First App

 

Building a Flutter App for Android The Holiday Shopping App.

by SysOps 0 Comments

Here it is a New Years and Iam still working with Flutter and Dart. Back before Christmas I started working with Flutter and built a few simple apps for my android phone. The first “real” app I created was for a friend. She is an artist in Arizona and blogs about art weekly. She’s been doing this for years. I host her site on my webserver, and help maintain her site. Working out issues when ever she wants a new gadget installed or the javascript she copied isn’t working for her.

So to help her out I wrote an android App that allows her followers to read her RSS feed. I plan on adding push notification to the app for her in the future. However, at this point that’s just a little beyond my knowledge of flutter and Android. With flutter I feel I am gaining knowledge quickly. I started my second app which required me to scape some data from an online database. The scrapping was easy using Python, Selenium, and BeautifulSoup. Tools I have plenty of experience with in the past. However, I soon realized that it was going to take some time to create booth a web api for the database and build the app. This app is a simple pet abuse lookup app. It’s allows people who are rehoming pets to check if the perspective new owner is in a criminal database for animal abuse. If they are, a summary of the public court records  can be seen.

So it being near christmas and me still needing an app that would be more complex yet not too complex to build. I settled on building a  Holiday Gift Shopping List App.   Below are a few screen shots. The current working model here still needs the database to be coded. At this point the app is working however it is working with a mock data adapter. A class that presents hard coded data in a maner similar to how I expect the database to work. if you look closely, you will see that if all the gift’s checkboxes are checked, the snowflake icon changes to a checkmark. The phone icon can be used to open the dialpadwith the user’s phone number ready for you to press call. The white + in the upper right open’s the user’s contacts so a Giftee may be added from the contact list. The Circle plus (+), let’s you add a gift idea to a user without opening the user gift list. Great for a quick idea!

All in all, the folks on gitter.im/flutter/flutter helped me out quite a bit. If you’re learning flutter there are quite a few helpful folks there. Icluding some members of the Flutter and Dart development teams.  They have all been very helpful. I’ve manage to help a few folks myself as I have learned Flutter and Dart. Which is nice because it helps drive home what you really know.

In the future I may do a tutorial on building the Holiday Gift App. For now, I’ve got other Flutter projects on the back burner and this one still needing the database connected. So, for now, I’ll complete this and move on.

Hope you all had a great New Years and a Wonderfully productive new year!

 

Designing The MiniMips – Part 4 Computer Architecture and Data Paths

by SysOps 0 Comments

Ok, last time we completed the basic design of our ALU. I hope you took the time to build it in Logisim and play with it. It should be trivial to comfirm it works by doing simple operations on paper or a programmer’s calculator and the ALU and comparing the results. Once you have confirmed that your ALU works correctly,  it is time to save it as a sub-circuit in Logisim. We’ll take a short step back to discuss the basic operation of a CPU and support systems and then we’ll answer to question of where our ALU input values and control signals come from and where the output goes.

Computer Architecture

Computers all follow a similar architecture. At a minimum they have Input, Processing, Storage, and Output. These components can take various forms. For example, input could come from a keyboard as with the typical personal computer, or it may come from some type of sensor such as the oxigen sensor in your car. Output could be to a display screen such as the LED monitor or a relay used to control a well pump. Memory can take the form of registers, magnetic disks, solid state disks, integrated circuits, or even paper punch cards. Processing can be either analog, digital, or even quantum devices.  However, the typical computer most people think of is the PC or typical desktop or laptop machine.

Computers systems can be categorized into many categories. Here we will place them into two broad categories based on the system architecture. Systems using seperate stores for instructions and data are said to be of a Hardvard Architecture. These machine read cpu instructions from ROM (Read Only Memory) and execute them. All data is stored in the system RAM (Random Access Memory). A typical microcontroller such as the Atmel AtMega324 is an example of this architecture. Other machines store cpuinstructions and data in the same memory. This allows for programs to become dynamic, being altered or created by the running program. This type of achitecture is known as Von Nueman Architecture. The Von Nueman Architecture is a powerful because program instructions are treated just like data. It does have it’s down sides however. One such short coming is the issue of security. Since any running program can access the program instructions, it can alter not only it’s own code but the code of other programs. So a rouge program could for instance alter the program for calculating an aircraft’s altitude. This could result in personal injury or death to the passengers. Because of this short-coming a lot of work has gone into developing ways to limit programs from having complete access to all programs stored in memory. Modern Von Nueman machnes contain a large amount of circuitry to manage program access to memory so such things wont occur.

Here we will develop a system using the Hardvardarchitecture and use a seperate instruction and data memories. This will allow us to reduce a few complexities and focus on other issues. In the future I may write another article on developing a Von Nueman machine.

CPU Architecture and Data Paths

Ok, let’s talk a bit about how a digital computer system works.  The first thing that occurs in most systems when power is applied is that the major components are set to a predetermined state. This reset also occurs when a “Reset” button is pushed. If you’ve ever had to take a paper click and hold in a tiny button on your router, then you’ve used a reset button. After the system has been reset, the cpu begins to step through it’s cycles. The first cycle is the ‘Fetch’ cycle. In this cycle the CPU fetches the next instruction from the program memory. Next, the instruction decoder decodes the instruction into the appropriate control signals for the various cpu components. The next step is to ‘Execute’ the instruction. The last step is to ‘Store’ the results in memory if required by the instruction. Most cpu instructions follow this pattern. However, this cycle can be shortened or have wait states added to it for complex instructions. In simple processesors some of these instructions are combined. Usually the Fetch and decode cycles are made to occur in the same cycle. In a single cycle cpu are these steps occur in one clock cycle. This is often accomplished by multiplying the clock input to generate the needed signals. However, in some architectures such as the one we will build, these steps can occur in a single cycle without the need for clock multiplication.

Below is the logisim circuit for the SimpleComputer.  This is the basic machine we will be building.  I’ve included some 7-Segment Hex digit displays to allow use to view the the CPU operation. I also included four ports on the ALU that allow us to connect some of those displays directly to the registers in the registerfile. While the circuit looks pretty basic here, it is only because I have used logisim’s ability to can a subcircuit as a component. So in this view all the major components are actually subcircuits that vary in complexity. They are all made out of logic gates of logisim standard components. If you have not used the Subcircuit feature in logisim I recommend you break out the tutorials and learn to use them. OK, let’s describe the circuit from a 10,000 foot level. We’ll being seeing each componet in great detail as we move forward.

The machine must first be reset using the reset button in the lower left of the circuit. Then we can single step through programs by clicking the clock input. We can run programs from the simulation menu in logisim. Once reset, the PC (Program Counter) will be set to 0x00 hexidecimal (hex). This will cause the first program instruction to be placed on the data output of the ROM (Read Only Memory). The ROM instruction code is broken into various parts that control certain aspects of the CPU. For eample, bits 0-3 of the instruction contain the ALU OP code. These four bits are used directly to select the chosen ALU operation. For shift operations bits 4-7 select the shift distance. For example if bits 4 – 7 contain the value 0010b in binary, then the value on the ALU’s A input will be shifted by two bit positions. Many bits in the instruction perform multiple duties. For eaxample, although bits 0-3 and 4-7 are used for the ALUOP code and the SHIFTAMT they also do double duty as the lower order byte of an immediate address in an instruction that contains an address value.

Once the instruction code is broken into it’s constituent parts, the various signals are reouted to other parts of the circuit as needed. A portion of the instruction code defines the type of instruction being executed. This allows the proper signals to be generated to control the parts of the circuit that are not directly controled by the bits in the instruction. Next the if the instruction is a register type instruction, the two source registers and the destination register are selected. The values in those registers and posibly an immediate value are feed into the ALU A and B inputs. The ALU then produces the out for all the operations we provided hardware for. This is done in parallele so producing all output at once isn’t an issue. The ALUOP code selects the desired function results and places them on the ALU output. Depending on the instruction the ALU ouput could be routed back to the destination register selected in the RegFile or it may be stored in RAM or used as a RAM address. It could also be used to set condition flags the determine if a branch will be taken. Lastly, it can be used as the input to the PC though that portion of the circuit is not shown here.

Look over the circuit and trace through the signal paths. See if you can figure out how the various parts work to control the circuit. Next time we’ll talk about the Instruction Set and continue on about the architecture. Until then, I hope you all have a happy new year!

 

 

Newsletter Powered By : XYZScripts.com