Simple Serial Endpoint Lab


Lab Overview
Creating and Disposing
Connecting and Disconnecting
Sending Data
Receiving Data
Other Things To Try...
Where To Go From Here...
Notes

Introduction
This lab takes you through building a simple serial endpoint. This code may be tested by connecting to a desktop machine running your favorite terminal emulation program. The interface for the Newton application has been completed and you will fill in the code to create, instantiate, connect, send and receive data, disconnect and dispose of the endpoint. It is a good idea to be comfortable with the code in this lab as later endpoint labs will assume a basic understanding of the techniques used here.

In order to make it possible to test code before the lab is completely finished, we have paired creating with disposing, connecting with disconnecting and finally, sending followed by receiving. This will allow you to write the code for each action and then test it without leaving the Newton in a "half open" state if your program crashes since you would then have to reset the machine before trying again. Of course if you crash in an interesting and creative way, you will have to reset anyway.

Included in this lab is the user proto, debugProto. This is essentially a debugger of last resort. It can be used to trace and record values to the Notepad application as your endpoint code runs. For more details on using it and other debugging tips, see the article Debugging Endpoints.

A quick summary of debugProto methods

As you go through this lab, the relevant methods in the lab project have comments on what code goes where. These comments all begin with three asterisks ("***") and are followed by a reference to the section of this document where the code is discussed. For example, the following comment is in the MSend method:
// *** send data and carriage return and linefeed (Sending Data)

which suggests what code should be added along with a reference back to the Sending Data section of this document.


Prerequisites
To do this lab you must have the following:
Files Needed
You must have the following files on the machine which has NTK on it:
1. The Simple Endpoint Lab files: Simple Serial project file, Main, Comms.rsrc, debugProto and constants.
2. Your favorite terminal emulation program in order to test Simple Serial once you've completed it. (A Claris Works document is available with all the correct settings in place if you wish to use it.)

Code Overview
Endpoints provide the basic API for all Newton communications programming. The specifics of the lifecycle of an endpoint is covered in the document Endpoint Details while a more high-level overview is provided in the article Endpoint Overview. The Simple Serial lab code has the user interface in place for a simple communications program which has a view for displaying incoming text and another for writing data you want to send out over the serial port as shown in the picture below:


"Simple Serial" Running on the Newton


It is recommended that you look at the code in Simple Serial before you begin programming. To do this, open the project and then open the Main layout browser and look at the base view (named Simple Serial). You will note that a number of methods (such as MConnect) have had the code removed. These are the methods you will be writing in this lab.

Note that there are separate methods for many of the phases in the lifecycle of an endpoint: MConnect, MSend, MDisconnect and MDisconnectCompProc. In addition, the slot MBaseEndpoint is used to define the basic endpoint while the methods MUpdateReceiving and MUpdateStatus are used to update information displayed on the screen.

Finally, the slot ep will hold the reference to the actual, live endpoint while the program is running. This use of a separate (initially nil) slot to hold the endpoint at runtime is a common technique in endpoint programming. Let's look at why.

Recall that NTK creates templates which are loaded into protected memory. These templates are therefore read-only (as anyone who has ever gotten a -48214 "Object is read only" error knows). If you declare an endpoint object in a template and then attempt to change any slot in the endpoint you will get a -48214 error. Instead, what we will do is to define the static features of the endpoint in the MBaseEndpoint slot and, at run time, define ep to be prototyped off of MBaseEndpoint. This allows us to create a dynamic endpoint frame which refers back to the MBaseEndpoint slot in the Simple Serial template but in which we can define dynamically changing slots.

The following figures show the difference:

Problem Caused by Defining Endpoint In Template



Solution: Create Dynamic ep Frame From Static Template Slot

In the first picture, the Simple Serial view has no staticEP slot. When staticEP.fred:=5 is executed, using the normal rules of inheritance, it looks for, and finds, a staticEP slot in the template. But trying to change a slot in the frame causes a -48214 error to be thrown since the template is in protected memory. We could of course clone all of staticEP so that it existed dynamically in Simple Serial view but if we did this we would unnecessarily using memory because most of an endpoint's definition is unchanging. Instead, in the second figure we have defined a slot ep which is assigned at runtime to be a frame with a _proto slot referring to the MBaseEndpoint slot which has the definition of the endpoint features. Now when we try to create or change the fred slot, since ep is in dynamic memory, the slot will be initialized or changed as we desire.

Trying The Solution
Before you work on coding this lab, you may want to run the solution code to see how the finished program works.

Before Starting...
Make sure the Newton is plugged into the desktop machine! The cable you use when programming with NTK will work nicely.

Also, make sure you have a terminal emulation program on your desktop machine which allows you to change the connection settings. The solution endpoint uses the following settings: 9600bps, 8 data bits, no parity, 1 stop bit, no flow control.

Next, using an appropriate program (e.g., Newton Program Installer (NPI)), download the Simple Serial package to the Newton.

On the Desktop Machine
Start the terminal emulation program, and, assuming the settings are correct, open a connection. For simple serial transfers (which this is), connection has nothing to do with talking to another machine, it simply means that the serial chip on your port can see the port on the Newton, a state which occurs whenever you're plugged into the Newton.

On the Newton
Launch Simple Serial from the Extras drawer. Tap the Connect button. Write some text into the send area and then tap the Send button. The text should show up in your terminal emulation window on the desktop machine. If it doesn't, check your connection settings and then try connecting again.

On the Desktop Machine
Type any phrase you wish followed by a return key. The text should appear in the received data area in Simple Serial. Note that no text appears until the carriage return key is typed. Notice also that if you follow up the first message with a second, you may see an unprintable character (displayed as a square) appear on the Newton at the start of this and all subsequent messages.

Question: what's that strange unprintable character ?

If everything worked according to plan, you should be able to disconnect from either side. You may also want to try downloading a small file from the desktop machine to the Newton, however, since we are not using any sort of flow control, it is easy to force an error on the Newton by overflowing the serial buffer on the port. If this occurs you will get an error slip on the Newton.

The first step in creating a working endpoint is to define the endpoint itself and instantiate it as a NewtonScript object. To do this the following things must be done:

1. The endpoint must be defined. This means creating a frame which specifies the type of endpoint (e.g., serial, AppleTalk, etc.), the connection parameters (e.g., baud rate, error correction, node name, etc.), and any other features (such as hardware port options). In addition it is common to define methods which will be used within the context of the endpoint.

2. Once defined, an endpoint object must be instantiated. This is a little unusual for most Newton programmers as unlike views, which are automatically instantiated by the Newton view system, endpoint objects must be explicitly instantiated.

3. Having been instantiated, it is necessary to go on and bind the endpoint in order to associate a local address (e.g., a node name) with the endpoint. Note that we must perform this binding even for endpoints which have no explicit associations. In other words, while AppleTalk has a node name which must be associated with the endpoint, there is no such obvious binding for serial endpoints.

Where Does It Go?
While there as many ways to structure endpoint code as there are programmers, the structure used in Simple Serial is typical of Newton communications programs and will be used repeatedly in this course.

Definition
Define the slot MBaseEndpoint to be a frame which specifies an asynchronous capable serial endpoint defined to run at 9600 baud, with 8 data bits, 1 stop bit and no parity. Make sure the frame proto's from protoBasicEndpoint, the generic 2.0 endpoint prototype. As noted in the Lab Overview, we will use this definition to instantiate a dynamic endpoint at runtime.

In the viewSetupDoneScript, assign the ep slot to be a frame proto'ed off of MBaseEndpoint. Also, define ep to be a child of the base view of the application. This last point is a minor addition which makes life much easier. At various times endpoint code will be called out of context of the application (say when an error occurs if we've defined an exception handler for the endpoint). In this case, by defining the endpoint frame to be a child of the base view, by virtue of parent inheritance, all the methods and variables defined in the base view will be accessible through the endpoint even though the Simple Serial application may not be in context.

By context here we mean, what application is running in the NewtonScript world

Instantiation
Once the endpoint is defined, it must be instantiated and bound. This code goes in the method MConnect (where connect will be put later). One thing which should be considered here is what to do if the attempt to instantiate the endpoint fails. In this case the endpoint system will normally throw an exception. We probably want to catch the error and notify the user of the problem immediately. The method MNotifyError will probably be useful to you.

Even more interesting is the situation which occurs after an attempt to bind an instantiated endpoint. Here we have to not only report the error, but we must also "back out" as gracefully as possible. What this means is that for every action that has been successfully completed in the endpoint life cycle, we must have code to undo that action. In this case, if binding fails we must remember to dispose of the instantiated endpoint.

To do these things we will use the exception handling mechanism as follows:
try
ep:PickAMethod();
on exception |evt.ex.comm| do // communications // exception only
begin
:MNotifyError(CurrentException().error);
// and anything else we want to do
end;

Disposing
Once connected, we're going to need to be able to dispose of the endpoint. This involves unbinding the endpoint as well as calling the dispose method. These calls should be put in the method MDisconnectCompProc which will be called after disconnecting has completed. If you are memory conscious, you may want to set the ep variable to nil after disposing the endpoint so that it doesn't stay in memory while the endpoint is not in use.

To test this software, add a call to MDisconnectCompProc in the MDisconnect method - we'll replace this with more elaborate code in the next section - and compile and download Simple Serial to the Newton. Once downloaded and with the serial cable connected to your desktop machine, run Simple Serial and tap the Connect button. Next tap the Disconnect button. If you have done the code correctly you should simply work without any error messages.

Don't forget that the Inspector uses the serial port!

For more information on defining and disposing of an endpoint you may want to look at the article Endpoint Details in this course or you may want to look at Chapter 4 of Apple's documentation on communications programming: Newton Programming: Communications.


Connecting
Once an endpoint has been instantiated and bound the next thing to do is connect it to the desktop machine. This code should be added in the MConnect method and, as before, if the attempt to connect fails, you should back out gracefully by calling MDisconnectCompProc which unbinds and disposes of the endpoint. In this lab we recommend doing a synchronous call to the Connect method.

If the connect attempt is successful you should set the vConnected instance variable (defined as nil in the base view) to true. We will use this variable later to decide what we can and can't do as we should not (for example) try sending data if we have not successfully connected the endpoint. Remember to put the attempt to connect within a try block in case a communications exception is thrown.

In addition to the actual call to connect the endpoint, remember that if the Newton goes to sleep, your connection will be lost. To avoid this you need to add a power off handler. This is a function which is called whenever the Newton is going to sleep in order to give you a chance to control what happens. In order to get this call you must "register" your poweroff handler using the system call RegPowerOff. We recommend you do this before you connect but you may also do it after the connect has completed successfully as you will have to remove it if the attempt to connect fails.

We have provided a power off handler in the method PowerOffScript. Take a look at it and make sure you understand it before using it. What it does is checks to see if we're connected (based on the vConnected variable). If we are, and if the Newton is going to sleep because it's been idle for too long, we tell the system to ignore the power off and stay awake. On the other hand, if the Newton is going to sleep because of some other reason (like the user is pressing the off switch or the Newton is running out of batteries), then we tell it to hold off going to sleep until we can disconnect the endpoint.

There is one "gotcha" to the power off script: it is called whenever the machine is going to sleep whether or not your application is the currently executing application. This means that it is called "out of context" for you application which means you can't find your instance variables and methods (such as vConnected of MDisconnect). To get around that you should define the call to PowerOffScript as a separate closure within the call to RegPowerOff. This is done as follows:
RegPowerOff(kAppSymbol, func(what, why)
:PowerOffScript(what, why); );
- which looks strange but is correct. The reason this works is that closures (as such functions are called) carry their context (i.e., who they are and where they live) with them so that they can find the referenced methods, etc. For more on closures see The NewtonScript Language Reference.

For more on power off functions, see Chapter 18 of Newton Programmer's Guide: System Software 2.0.

Disconnecting
Disconnecting is a little more complicated. Since the MDisconnect method is called after a successful connection has occurred, and since after disconnect we are going to go on to unbind and dispose of the endpoint we must make sure that the we have completely disconnected before we call the MDisconnectCompProc. Most production level communications programs use an asynchronous disconnect call to avoid hanging the machine if there's a problem and to provide as responsive a system as possible but for now we recommend you simply make a synchronous call to Disconnect followed by a call to MDisconnectCompProc.

Before you disconnect however, you should cancel any pending actions to make sure the endpoint is in a state where it can be disconnected. To do this add a Cancel call in MDisconnect.

You should also remember to unregister the power off handler you installed during the connect phase using the system call UnRegPowerOff and you should clear the vConnected variable by resetting it to nil.

Finally, remember to call MDisconnect if the user quits the application. Add this method call to the viewQuitScript for the base view.

Once you have written this code you may check it by compiling and downloading your new version of Simple Serial. If it works, then by pressing the connect button you should change the status to "Ready to send and receive," which is not strictly true but we'll be adding the necessary code soon. Remember to check your power off code. Correct behavior means that your unit will not go to sleep if left idle but will disconnect the endpoint if the user depresses the "power off" button.

For more background on connecting and disconnecting, as well as the power off function you may want to take a look at the Endpoint Details article. Or you may want to refer back to Apple's documentation, Newton Programming: Communications.


Sending string data is relatively straightforward on the Newton. The text entered can be gotten as follows:
OutputArea.text
The code to send this text should go into the MSend method. You may want to add a line feed character (unicodeLF) and a carriage return character (unicodeCR) to the string in order to avoid having sequential lines run together. Remember that since NewtonScript objects carry type information around with them you will not normally need to specify how they are sent unless you want to send them in a way that is different from the default for that type.

Be careful of using a lot synchronous Output calls.

As mentioned previously we should check the vConnected variable to make sure we're connected before we try to send data. If you forget this step you may find yourself exercising your error handling code if the user tries to send without connecting.

When this code is completed you should be able to run the terminal emulator on your desktop machine, connect it and send strings of text from the Newton to it by entering them into the send area and tapping the Send button. If nothing reaches your desktop machine, check that your cable is connected, then check the settings of your terminal emulation program (it should be 9600bps, 8 data bits, 1 stop bit, no parity, no flow control) and finally review your connection code on the Newton to make sure that it matches these connection specifications.

For more information about outputting data, you may want to review the article Endpoint Details or you may want to look at Chapter 4 of Newton Programming: Communications.


Receiving data is not quite as straightforward as sending data. Since data may arrive in at any time relative to what's happening in our application, we will specify what data we're looking for and what method should be called when it arrives.

This is done by specifying an inputSpec frame. Probably the simplest way to do this is to add it into MBaseEndpoint and then, in MConnect, call SetInputSpec to notify the endpoint system of what to do with input for your endpoint.

The input spec frame must have at least three things: the form of the data, the termination condition and the InputScript which will be called when the termination condition is met. For the purposes of this lab, we recommend a string input type that looks for a carriage return character and which, when such a string is received, puts the string into the receiving area by calling the MUpdateReceiving method.

You may also want to avoid overflowing the input buffer in case you never see a carriage return character by setting the discardAfter slot of the input spec frame.

When this code is completed you should be able to receive text from the terminal emulation program on your desktop machine after you have connected the Newton. To test, connect the terminal emulation program. You should be able to just type a phrase on the desktop machine and it should appear on the Newton after you press the return key. If nothing appears, you may want to check what your terminal emulation program does with the return key. Some programs send only linefeed characters, some send only carriage return characters, some send both and some are user settable.

For more information about receiving data, you may want to review the article Endpoint Details or you may want to look at Chapter 4 of Newton Programming: Communications.


The endpoint implemented in this lab is extremely simple. There are many other things you can add to it. Here is a partial list of some things you may want to try:

1. Add an exception handler. Endpoints can be defined with their own exception handlers which are called when the endpoint throws an error. While we have put most of the critical endpoint calls into try blocks whose exception handling supersedes the endpoint exception handler, you will want to have an exception handler explicitly defined for the endpoint before making asynchronous calls since the execution of an asynchronous call occurs outside of the context of the application and any try blocks it has added. This code should be added as a function in the base definition of the endpoint.

2. You may want to start adding asynchronous calls by making the calls to Connect and Disconnect asynchronous. To do this you will need to become familiar with the completion frames described both in Endpoint Details and in Chapter 4 of the communications documentation Newton Programming: Communications.

3. If you persistently have trouble receiving data, you need to know whether the problem is that you are not getting any data at all at the Newton end or that you are not triggering your input spec. Perhaps the easiest way to do this is to add a partial script to your input spec. This is a script which is called periodically at time intervals specified by you, which passes the contents of the input buffer. If you are not getting anything in the input buffer, you should go through the check of the cable and connection settings and check that you were not getting any errors from defining, instantiating, binding and connecting the endpoint.


This is the first lab in the Endpoint section of the online course: Newton Programming: 2.0 Communications. See the course road map to navigate among the different topics pictured.

More directly:

If you want an overview about endpoint programming, see the article Endpoint Overview.

If you want more details about endpoints, see the article: Endpoint Details.

If you want to get the solution code for this lab, go to Simple Serial Code.

For more on endpoint state machines, see the online article: An Introduction To Communications State Machines.

For help debugging endpoint code, look at the article Debugging Endpoints.

For more specialized information about endpoints, try the following articles:
Endpoint Data Forms
Other Types of Endpoints
For a detailed walkthrough of an existing endpoint sample, try the Walkthrough of Basic Modem.

For specific details about converting endpoint code from the 1.x Newton communications system, try the lab: Converting From 1.x Endpoints to 2.0 Endpoints.


Here is a quick summary of the methods available in the debugProto. Note that debugProto is based on protoApp so it is of limited utility beyond debugging communications code.

debugProto Quick Reference
TraceToNote(message,variable,tonote,toglance) // output message+variable to note // or protoGlance
SetDbugPDepth(newdepth) // change printing depth for frames, defaults to 1

NewNote() // end previous note so tracing goes to new note

TurnTraceOn(flag) // turn all tracing on/off

So, for example, in viewSetupForm script we might call :TurnTraceOn(true) to actually start outputting to a note. Then, in MConnect, we might make the following call :TraceToNote("In Connect, ep=",ep,true,nil). Before making that call, we might want to change the depth of information displayed to see more of the subframes. To do this we would call :SetDbugPDepth(2).



It's the newline character. The Newton is triggering input handling on the carriage return character but many terminal emulation programs send both a carriage return and a newline character. We aren't looking for this so the newline character remains in the buffer until the next stream of characters arrive at which point it is at the front of the incoming message (since it was there first!).



By context what we mean is that the exception handler for an endpoint is called when the application (Simple Serial) is not the active application. It is a case when the low-level communications tool causes the NewtonScript system to call the exception handler even if Simple Serial is not running, Note that the NewtonScript system is still a single thread (i.e., it is not multi-tasking) but that the Newton OS itself is multitasking so that the low-level communication tool is asynchronous relative to the application. For more detail on the relationship between endpoints and the underlying comm tools, see the Q&A titled "What Really Happens During Instantiate and Connect."



Remember that NTK's Inspector uses the serial port. If you're used to connecting the Inspector so that NTK will compile and automatically download the newly compiled package, and you forget to disconnect the Inspector, attempting to bind Simple Serial's endpoint will fail as the port is already open to communicate with the Inspector.



Successive calls to Output may cause you to run out of memory as each call allocates a separate "sub-task" which will only get released after you have returned to an idle state in your application. For this reason most applications will send data asynchronously. However, for this lab we're doing everything synchronously. Since all we are sending is one string at a time we should be fine. For more details about this, see the Q&A "Why Synchronous Comms Are Evil."

Navigation graphic, see text links

Developer Services | Technical Information | Tools | Sample Code

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help


Copyright Apple Computer, Inc.
Maintained online by commscourse@newton.apple.com
Updated 26-Sep-96 by dcd