<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Zip &#8211; Henry Poon&#039;s Blog</title>
	<atom:link href="https://blog.henrypoon.com/blog/tag/zip/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.henrypoon.com</link>
	<description></description>
	<lastBuildDate>Tue, 04 Oct 2022 21:58:03 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
<site xmlns="com-wordpress:feed-additions:1">83044199</site>	<item>
		<title>Jenkins pipeline for Spring with beta and prod stages and deployment rollback</title>
		<link>https://blog.henrypoon.com/blog/2017/03/09/jenkins-pipeline-for-spring-with-beta-and-prod-stages-and-deployment-rollback/</link>
					<comments>https://blog.henrypoon.com/blog/2017/03/09/jenkins-pipeline-for-spring-with-beta-and-prod-stages-and-deployment-rollback/#respond</comments>
		
		<dc:creator><![CDATA[hp]]></dc:creator>
		<pubDate>Thu, 09 Mar 2017 08:04:51 +0000</pubDate>
				<category><![CDATA[computer stuff]]></category>
		<category><![CDATA[9]]></category>
		<category><![CDATA[Apache Ant]]></category>
		<category><![CDATA[Apache Maven]]></category>
		<category><![CDATA[Build automation]]></category>
		<category><![CDATA[Chan]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[Compiling tools]]></category>
		<category><![CDATA[Computing]]></category>
		<category><![CDATA[Const]]></category>
		<category><![CDATA[Continuous integration]]></category>
		<category><![CDATA[D]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Digital technology]]></category>
		<category><![CDATA[Download]]></category>
		<category><![CDATA[EAR]]></category>
		<category><![CDATA[future]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[groovy]]></category>
		<category><![CDATA[Hole]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[IME]]></category>
		<category><![CDATA[It]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Java enterprise platform]]></category>
		<category><![CDATA[Jenkins]]></category>
		<category><![CDATA[Levant]]></category>
		<category><![CDATA[Password]]></category>
		<category><![CDATA[PATH]]></category>
		<category><![CDATA[Plug]]></category>
		<category><![CDATA[Port]]></category>
		<category><![CDATA[Reference]]></category>
		<category><![CDATA[Rolling]]></category>
		<category><![CDATA[Run]]></category>
		<category><![CDATA[Scripting languages]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[STR]]></category>
		<category><![CDATA[Thou]]></category>
		<category><![CDATA[Time]]></category>
		<category><![CDATA[URL]]></category>
		<category><![CDATA[Variable]]></category>
		<category><![CDATA[Z]]></category>
		<category><![CDATA[Zip]]></category>
		<guid isPermaLink="false">https://blog.henrypoon.com/?p=2614</guid>

					<description><![CDATA[In the past couple of days, I&#8217;ve been experimenting a bit with the Jenkins pipeline plugin to create a code deployment pipeline with independent beta and prod stages for a Spring Boot app. I even managed to add rolling back a deployment in case a prod deployment fails! It took me a bit of time [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In the past couple of days, I&#8217;ve been experimenting a bit with the <a href="https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Plugin" target="_blank" rel="noopener">Jenkins pipeline plugin</a> to create a code deployment pipeline with independent beta and prod stages for a Spring Boot app. I even managed to add rolling back a deployment in case a prod deployment fails! It took me a bit of time to Google my way through how to do everything, so I figure I just lay it all out here in case it helps other people do the same thing.</p>



<p class="wp-block-paragraph">The nice thing about all of this is that I can push a code change to git, and Jenkins can build it, run through all the tests and then deploy to production automatically.</p>



<h1 class="wp-block-heading">Layout of a pipeline script</h1>



<p class="wp-block-paragraph">Pipeline scripts are written in <a rel="noopener" href="http://www.groovy-lang.org/" target="_blank">Groovy</a>, which is a variant of Java. In general, a pipeline script is laid out like this:</p>



<pre class="wp-block-code"><code lang="python" class="language-python">node {
    def SOME_CONSTANT = "whatever"
    ...

    stage('some stage name like Build') {
        // Stuff to do as a part of this stage
    }

    stage('another stage') {
        // More stuff
    }

    ...
}</code></pre>



<p class="wp-block-paragraph">Each stage represents a stage in the pipeline (e.g. building, beta deployment, prod deployment etc.)</p>



<h1 class="wp-block-heading">Defining some constants</h1>



<p class="wp-block-paragraph">First, a list of constants can be defined that can be used throughout the pipeline so that if the pipeline script gets reused somewhere, only these constants have to be changed. I&#8217;m not aware of anything that lets me reuse a pipeline in Jenkins without copying and pasting the code somewhere else so for now I&#8217;ll have to live with copying and pasting.</p>



<p class="wp-block-paragraph">My project uses Maven so I got Jenkins to download its own copy of Maven that it can use to execute builds and have defined it as a constant. The name I&#8217;ve given it in the Jenkins Global Tool Configuration is &#8220;Maven 3.3.9&#8221; exactly. My project also uses Tomcat so there are some Tomcat specific things in there that may or may not be relevant to your use case.</p>



<pre class="wp-block-code"><code lang="python" class="language-python">def MAVEN_HOME = tool 'Maven 3.3.9'
    def WORKSPACE = pwd()

    def PROJECT_NAME = "name-of-project"
    def WAR_PATH_RELATIVE = "App/target/${PROJECT_NAME}.war"
    def WAR_PATH_FULL = "${WORKSPACE}/${WAR_PATH_RELATIVE}"
    def TOMCAT_CTX_PATH_BETA = "Tomcat-context-path-for-the-beta-stage"
    def TOMCAT_CTX_PATH_PROD = "Tomcat-context-path-for-the-prod-stage"
    def GIT_REPO_URL = "URL-to-git-repo-ending-in-.git"</code></pre>



<h1 class="wp-block-heading">Preparation Stage</h1>



<p class="wp-block-paragraph">First, the code has to be retrieved from the repository before it gets built. Jenkins allows storing username/password pairs so that they can be referenced without having to write out the password in plaintext. Jenkins uses a &#8220;credential ID&#8221; for this.</p>



<pre class="wp-block-code"><code lang="python" class="language-python">    stage('Preparation') {
        git branch: "master",
        credentialsId: "credentials-ID-stored-in-Jenkins-that-can-access-the-git-repo",
        url: "${GIT_REPO_URL}"
    }</code></pre>



<h1 class="wp-block-heading">Build Stage</h1>



<p class="wp-block-paragraph">Next, the code must be built and unit tested. &#8220;mvn clean install&#8221; will do just that (depending on what you want, you can always put in a different maven goal). The junit command is just there to take the resulting XML that gets generated during the build process and posts a graph of how many tests were run for each build.</p>



<pre class="wp-block-code"><code lang="python" class="language-python">    stage('Build') {
        sh "'${MAVEN_HOME}/bin/mvn' clean install"
        junit '**/target/surefire-reports/TEST-*.xml'
    }
</code></pre>



<h1 class="wp-block-heading">Beta Stage</h1>



<p class="wp-block-paragraph">Once the build succeeds, you&#8217;ll want to deploy it to a beta environment, so integration tests can happen. The following happens at this stage:</p>



<ol class="wp-block-list"><li>Get the right credentials to get permissions to Tomcat</li><li>Call the deploy method to deploy the war file in Tomcat (more on that later)</li><li>If the deployment fails for whatever reason, print out the deployment log for debugging and fail the build</li><li>If the deployment succeeds, run the integration tests (the command to do this may differ based on use case)</li></ol>



<pre class="wp-block-code"><code lang="python" class="language-python">    stage('Beta') {
        withCredentials([[$class: 'UsernamePasswordMultiBinding',
            credentialsId: 'credential-id-for-tomcat',
            usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
                // Password is available as an env variable, but will be masked 
                // if you try to print it out any which way
                def output = deploy(WAR_PATH_FULL, TOMCAT_CTX_PATH_BETA,
                        env.USERNAME, env.PASSWORD)
                if (output.contains("FAIL - Deployed application at context path " + 
                        "/${TOMCAT_CTX_PATH_BETA} but context failed to start")) {
                    echo "----- Beta deployment log -----"
                    echo output
                    echo "-------------------------------"
                    currentBuild.result = 'FAILURE'
                    error "Beta stage deployment failure"
                }
            }

        echo "Running integration tests"
        sh "'${MAVEN_HOME}/bin/mvn' -Dtest=*IT test"
        junit '**/target/surefire-reports/TEST-*.xml'
    }</code></pre>



<p class="wp-block-paragraph">The deploy method is what takes care of the actual deployment to Tomcat, which is how the Jenkins build tells Tomcat about the newly built war file. The deploy method is below (be sure to change the server IP and port). Alternatively, I could have built my project as an embedded jar file, but that has a different challenge in figuring out how to get Jenkins to tell the OS to execute the newly built jar file as a particular user.</p>



<ol class="wp-block-list"><li>Based on the Tomcat context path, decide whether a beta or production deployment is happening</li><li>Make a copy of the build war file and add .prod or .beta to the end of it so as to keep the original</li><li>Set the Spring profile to use on the newly copied war file (more on that later)</li><li>Call the curl command to do the actual deployment to Tomcat (more on that later)</li></ol>



<pre class="wp-block-code"><code lang="python" class="language-python">def deploy(warPathFull, tomcatCtxPath, username, password) {
    def envSuffix = ""
    def isBeta = tomcatCtxPath.contains("beta")
    if (isBeta) {
        envSuffix = "beta"
    } else {
        envSuffix = "prod"
    }
    sh script: "cp ${warPathFull} ${warPathFull}.${envSuffix}" 
    setSpringProfile(warPathFull, isBeta)
    def output = sh script: "curl --upload-file '${warPathFull}.${envSuffix}' " +
            "'http://${username}:${password}@localhost:8081/manager/text/deploy" + 
            "?path=/${tomcatCtxPath}&amp;update=true'", returnStdout: true
    return output
}</code></pre>



<p class="wp-block-paragraph">In the case of my project, I&#8217;m building a single war file that does not have a Spring profile (beta/prod) defined. This means that I have to manually define this before I deploy the app to Tomcat since there are some things that differ between beta and prod like database URL&#8217;s. To do this, I wrote a method that opens the war file like a zip (jar/war files are zip files) and adds a line to my application.properties to define a Spring profile.</p>



<p class="wp-block-paragraph">Admittedly, doing this zip file manipulation seems kind of hacky. Alternatively, I could have defined my build such that I had a separate beta build and a prod build to avoid modifying the zip file, but the drawback is that I&#8217;d then have to build my code twice.</p>



<pre class="wp-block-code"><code lang="python" class="language-python">def setSpringProfile(warPathFull, isBeta) {
    def zipFileFullPath = warPathFull + "." + (isBeta ? "beta" : "prod")
    def zipIn = new File(zipFileFullPath)
    def zip = new ZipFile(zipIn)
    def zipTemp = File.createTempFile("temp_${System.nanoTime()}", 'zip')
    zipTemp.deleteOnExit()
    def zos = new ZipOutputStream(new FileOutputStream(zipTemp))
    def toModify = "WEB-INF/classes/application.properties"

    for(e in zip.entries()) {
        if(!e.name.equalsIgnoreCase(toModify)) {
            zos.putNextEntry(e)
            zos &lt;&lt; zip.getInputStream(e).bytes
        } else {
            zos.putNextEntry(new ZipEntry(toModify))
            zos &lt;&lt; zip.getInputStream(e).bytes
            zos &lt;&lt; ("\nspring.profiles.active=" + (isBeta ? "beta" : "prod")).bytes
        }
        zos.closeEntry()
    }

    zos.close()
    zipIn.delete()
    zipTemp.renameTo(zipIn)
}</code></pre>



<p class="wp-block-paragraph">A curl command to Tomcat is what actually does the deployment. To deploy a file to Tomcat, do the following below. This will deploy the war file to Tomcat and instantly run it, thus it will be accessible at the given context path.</p>



<pre class="wp-block-code"><code lang="bash" class="language-bash">curl --upload-file 'path-to-war-file' http://username:password@server-address:port/manager/text/deploy?path=/tomcat-context-path&amp;update=true</code></pre>



<h1 class="wp-block-heading">Prod Stage</h1>



<p class="wp-block-paragraph">The same kind of stuff happens in the prod stage as in the beta stage with a few exceptions. The following happens at this stage:</p>



<ol class="wp-block-list"><li>Get the right credentials to get permissions to Tomcat</li><li>Call the deploy method to deploy the war file in Tomcat (except this time it is prod)</li><li>If the deployment fails for whatever reason, print out the deployment log for debugging and roll back the deployment</li><li>If the deployment succeeds, save the build files, and then the pipeline is finished. Alternatively, smoke tests can be run at this point, but I did not implement this in my project</li></ol>



<p class="wp-block-paragraph">Rollback is important because if the deployment fails, you don&#8217;t want to be stuck with a broken environment. Since build artifacts are saved on successful deployments, these same artifacts can be brought back if future deployments fail. This means they can be redeployed so that the code can be fixed before another deployment happens.</p>



<pre class="wp-block-code"><code lang="python" class="language-python">    stage('Prod') {
        withCredentials([[$class: 'UsernamePasswordMultiBinding',
            credentialsId: 'credential-id-for-tomcat',
            usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
                // Password is available as an env variable, but will be masked 
                // if you try to print it out any which way
                def output = deploy(WAR_PATH_FULL, TOMCAT_CTX_PATH_PROD, env.USERNAME, env.PASSWORD)
                if (output.contains("FAIL - Deployed application at context path " + 
                        "/${TOMCAT_CTX_PATH_PROD} but context failed to start")) {
                    echo "Prod stage deployment failure, rolling back deployment"
                    echo "----- Prod deployment log -----"
                    echo output
                    echo "-------------------------------"
                    step([$class: 'CopyArtifact',
                            filter: "${WAR_PATH_RELATIVE}",
                            fingerprintArtifacts: true,
                            projectName: "${PROJECT_NAME}",
                            target: "${WAR_PATH_RELATIVE}.rollback"])
                    deploy(WAR_PATH_FULL + ".rollback/" + WAR_PATH_RELATIVE,
                            TOMCAT_CTX_PATH_PROD, env.USERNAME, env.PASSWORD)
                    currentBuild.result = 'FAILURE'
                    error "Prod deployment rolled back"
                } else {
                    archiveArtifacts artifacts: "${WAR_PATH_RELATIVE}*", fingerprint: true
                }
            }
    }
</code></pre>



<p class="wp-block-paragraph">At the end you get to have something like this:</p>


<div class="wp-block-image">
<figure class="aligncenter"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="600" height="882" src="https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2017/03/JenkinsPipeline.png?resize=600%2C882&#038;ssl=1" alt="" class="wp-image-2638"/></figure>
</div>


<p class="wp-block-paragraph">That pretty much sums up the whole Jenkins pipeline that I&#8217;ve been using lately for Spring projects!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.henrypoon.com/blog/2017/03/09/jenkins-pipeline-for-spring-with-beta-and-prod-stages-and-deployment-rollback/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2614</post-id>	</item>
		<item>
		<title>Repairing corrupt PowerPoint files</title>
		<link>https://blog.henrypoon.com/blog/2015/05/13/repairing-corrupt-powerpoint-files/</link>
					<comments>https://blog.henrypoon.com/blog/2015/05/13/repairing-corrupt-powerpoint-files/#comments</comments>
		
		<dc:creator><![CDATA[hp]]></dc:creator>
		<pubDate>Thu, 14 May 2015 05:39:21 +0000</pubDate>
				<category><![CDATA[computer stuff]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[Computer]]></category>
		<category><![CDATA[Computer file]]></category>
		<category><![CDATA[corrupt powerpoint]]></category>
		<category><![CDATA[corrupt pptx]]></category>
		<category><![CDATA[corrupt zip]]></category>
		<category><![CDATA[D]]></category>
		<category><![CDATA[diskinternals]]></category>
		<category><![CDATA[EAR]]></category>
		<category><![CDATA[ise]]></category>
		<category><![CDATA[It]]></category>
		<category><![CDATA[Microsoft PowerPoint]]></category>
		<category><![CDATA[Office software]]></category>
		<category><![CDATA[Office work]]></category>
		<category><![CDATA[Over]]></category>
		<category><![CDATA[Thou]]></category>
		<category><![CDATA[Z]]></category>
		<category><![CDATA[Zip]]></category>
		<category><![CDATA[zip repair]]></category>
		<guid isPermaLink="false">https://www.henrypoon.com/blog/?p=2084</guid>

					<description><![CDATA[A free tool called DiskInternals ZIP Repair&#160;was able to recover a corrupted PowerPoint&#160;file (*.pptx) that I had on my computer. &#160;I learned that pptx files are actually zip files in disguise, and so using a utility to repair a corrupt zip archive could work. &#160;At first I thought it was one of those sketchy bloatware [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>A free tool called <a href="http://www.diskinternals.com/zip-repair" target="_blank" rel="noopener noreferrer">DiskInternals ZIP Repair</a>&nbsp;was able to recover a corrupted PowerPoint&nbsp;file (*.pptx) that I had on my computer. &nbsp;I learned that pptx files are actually zip files in disguise, and so using a utility to repair a corrupt zip archive could work. &nbsp;At first I thought it was one of those sketchy bloatware programs, but Lifehacker has written an <a href="http://lifehacker.com/5645798/diskinternals-zip-repair-fixes-up-your-faulty-downloads" target="_blank" rel="noopener noreferrer">article</a> about them before, so it should be fine.</p>
<p>Using the program was pretty simple. &nbsp;I just opened it and told it which zip file (in my case the pptx file which I renamed to a zip file), and then the program did all the work and recovered everything. &nbsp;It was like magic!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.henrypoon.com/blog/2015/05/13/repairing-corrupt-powerpoint-files/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2084</post-id>	</item>
		<item>
		<title>Setting up Karaoke (KTV) like an actual Karaoke place at home using JetKTV</title>
		<link>https://blog.henrypoon.com/blog/2012/01/02/setting-up-karaoke-ktv-like-an-actual-karaoke-place-at-home-using-jetktv/</link>
					<comments>https://blog.henrypoon.com/blog/2012/01/02/setting-up-karaoke-ktv-like-an-actual-karaoke-place-at-home-using-jetktv/#comments</comments>
		
		<dc:creator><![CDATA[hp]]></dc:creator>
		<pubDate>Tue, 03 Jan 2012 03:00:05 +0000</pubDate>
				<category><![CDATA[computer stuff]]></category>
		<category><![CDATA[.exe]]></category>
		<category><![CDATA[9]]></category>
		<category><![CDATA[agra]]></category>
		<category><![CDATA[AppLocale]]></category>
		<category><![CDATA[Artists]]></category>
		<category><![CDATA[Asian culture]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Bopomofo]]></category>
		<category><![CDATA[Chan]]></category>
		<category><![CDATA[Chinese language]]></category>
		<category><![CDATA[Click]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[Computer]]></category>
		<category><![CDATA[Contents]]></category>
		<category><![CDATA[Culture]]></category>
		<category><![CDATA[D]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[Dia]]></category>
		<category><![CDATA[Directory]]></category>
		<category><![CDATA[Download]]></category>
		<category><![CDATA[DVD]]></category>
		<category><![CDATA[EAR]]></category>
		<category><![CDATA[English language]]></category>
		<category><![CDATA[Find]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[IME]]></category>
		<category><![CDATA[It]]></category>
		<category><![CDATA[Japan]]></category>
		<category><![CDATA[Karaoke]]></category>
		<category><![CDATA[Language]]></category>
		<category><![CDATA[Light]]></category>
		<category><![CDATA[Link]]></category>
		<category><![CDATA[Literature]]></category>
		<category><![CDATA[Lyrics]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Net]]></category>
		<category><![CDATA[Password]]></category>
		<category><![CDATA[PATH]]></category>
		<category><![CDATA[Pho]]></category>
		<category><![CDATA[Plan]]></category>
		<category><![CDATA[Plug]]></category>
		<category><![CDATA[Prompt]]></category>
		<category><![CDATA[Reference]]></category>
		<category><![CDATA[Simplified Chinese characters]]></category>
		<category><![CDATA[Singing]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Songs]]></category>
		<category><![CDATA[STR]]></category>
		<category><![CDATA[Struct]]></category>
		<category><![CDATA[taiwan]]></category>
		<category><![CDATA[Taiwanese culture]]></category>
		<category><![CDATA[Thou]]></category>
		<category><![CDATA[Time]]></category>
		<category><![CDATA[Trac]]></category>
		<category><![CDATA[UNC]]></category>
		<category><![CDATA[Vol]]></category>
		<category><![CDATA[Volume]]></category>
		<category><![CDATA[Websites]]></category>
		<category><![CDATA[YouTube]]></category>
		<category><![CDATA[Z]]></category>
		<category><![CDATA[Zip]]></category>
		<guid isPermaLink="false">http://henrypoon.mooo.com/blog/setting-up-karaoke-ktv-like-an-actual-karaoke-place-at-home-using-jetktv</guid>

					<description><![CDATA[As many people are aware, Karaoke is popular among Asian people.&#160; Generally, people go to a Karaoke establishment to enjoy it, but it can also be done at home.&#160; Current methods involve juggling a bunch of VCD’s, DVD’s, or even LD’s to get the wanted song.&#160; Karaoke establishments have all set up systems for people [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">As many people are aware, Karaoke is popular among Asian people.&nbsp; Generally, people go to a Karaoke establishment to enjoy it, but it can also be done at home.&nbsp; Current methods involve juggling a bunch of VCD’s, DVD’s, or even LD’s to get the wanted song.&nbsp; Karaoke establishments have all set up systems for people to use a computer to choose a song from a database (by artist, name, gender, etc.) that will be played on the TV.&nbsp; This set up can be replicated at home.</p>



<p class="wp-block-paragraph">This guide presents how to mimic the system used in professional karaoke establishments at home.&nbsp; The software this system uses revolves around a Taiwanese program called JetKTV.&nbsp; Much of the content from this guide was drawn from Chinese language websites discussing the usage of this program (scroll all the way down for References).&nbsp;&nbsp;&nbsp; There is little literature on this subject in English and so this guide presents basically an English version of the reference sites plus a few added notes.</p>



<p class="wp-block-paragraph">The program used is in Chinese, so people who are not fluent in Chinese may have a hard time navigating through the software.&nbsp; Those who are brave enough to continue or have a basic knowledge of Chinese with better English fluency may find it helpful to see English instructions. &nbsp;The Chinese sites on this subject are also written for older versions, and the setup procedure for those older versions are slightly different.</p>


<div class="wp-block-image">
<figure class="aligncenter"><a href="https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2012/01/jetktv.png?ssl=1" target="_blank" rel="noreferrer noopener"><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2012/01/jetktv_thumb.png?w=1280&#038;ssl=1" alt="jetktv" title="jetktv"/></a></figure>
</div>


<h1 class="wp-block-heading">Contents</h1>



<ul class="wp-block-list"><li><a href="#tvsetup">Proposed Setup</a></li><li><a href="#jetktvsetup">Setting Up JetKTV2010</a></li><li><a href="#insertsong">Inserting A New Song</a></li><li><a href="#insertartist">Inserting A New Artist</a></li><li><a href="#testing">Testing</a></li><li><a href="#ref">References</a></li></ul>



<h1 class="wp-block-heading"><a name="tvsetup"></a>Proposed Setup</h1>



<p class="wp-block-paragraph">The proposed setup of all the hardware (TV’s, amps, computer, etc.) is in the diagram below.</p>


<div class="wp-block-image">
<figure class="aligncenter"><a href="https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2012/01/ksetup.png?ssl=1" target="_blank" rel="noreferrer noopener"><img data-recalc-dims="1" decoding="async" src="https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2012/01/ksetup_thumb.png?w=1280&#038;ssl=1" alt="ksetup" title="ksetup"/></a></figure>
</div>


<p class="wp-block-paragraph">The computer will play the chosen Karaoke videos and transmit the video signal to the TV (via extended display like in a dual monitor set up).&nbsp; The computer’s audio will go to an amplifier or a mixer, which is then transmitted to a set of speakers.&nbsp; Microphones are plugged into the amplifier/mixer is well so that the speakers can also play the sound picked up by the microphones.&nbsp; Depending on the hardware, the cables could be different (some TV’s may not have HD output etc.)The&nbsp; computer is the source of all the signals transmitted to the other devices and must be set up with the Karaoke software.</p>



<h1 class="wp-block-heading"><a name="jetktvsetup"></a>Setting up JetKTV2010</h1>



<p class="wp-block-paragraph">The PC will use the following software:</p>



<ul class="wp-block-list"><li>JetKTV2010 (<a rel="noopener" href="https://skydrive.live.com/?cid=46746bb36e167913&amp;sc=documents&amp;id=46746BB36E167913!171" target="_blank">link</a>)</li><li>SongMgr (optional – not helpful on English language PC’s – more on that topic later)</li></ul>



<p class="wp-block-paragraph">It is helpful to set up the software using a dual monitor setup.&nbsp; That way, it is easier to test without having to go and plug the computer to the TV each time.</p>



<p class="wp-block-paragraph">Unzip the contents of the JetKTV2010 program in a folder and open JetKTV2010.exe.&nbsp; The GUI buttons are for searching through the database (by looking for the artist, song name, etc.) to find the wanted song. Once a song is selected, it will be added to the list of songs to play just like the software at actual Karaoke establishments. The video will play in full screen on one monitor and the song picker GUI will say on another monitor.&nbsp; To close the program, click on the top left corner of the GUI (hidden button).</p>



<p class="wp-block-paragraph">Some of the software features in addition to searching and adding songs:</p>



<ul class="wp-block-list"><li>Skipping songs</li><li>Fast forwarding, pausing, etc.</li><li>Switching from one audio channel to both audio channels (alternating between vocal on/off)</li></ul>



<p class="wp-block-paragraph">This program reads from a database that contains the song names, artists, language, etc.&nbsp;&nbsp; Therefore, <strong><em>it does not come with songs</em></strong>.&nbsp; Songs must be downloaded separately and added on.&nbsp; These can come from existing DVD’s or YouTube.&nbsp; The next section will explain how to populate the database.</p>



<h1 class="wp-block-heading">Populating the Database</h1>



<p class="wp-block-paragraph">The SongMgr program mentioned above can add/remove contents from the database, but there are problems with it when using it on English language PC’s due to problems in encoding some of the Chinese characters.&nbsp; Even Microsoft AppLocale fails to rectify the problems.&nbsp; The solution is to use Microsoft Access to open up the database file directly and make changes.</p>



<p class="wp-block-paragraph">Adding one song can seem like a lengthy process at the first try, but it will get easier as one becomes more familiar with the system.</p>



<h3 class="wp-block-heading"><a name="insertsong"></a>Inserting A New Song</h3>



<p class="wp-block-paragraph">To insert a new song, one must navigate to the table where the songs are stored and then add an entry to it.</p>



<ol class="wp-block-list"><li>Open <em>Song.mdb</em>in the JetKTV program directory</li><li>When prompted for a password, input “tmwcmgumbonqd” without quotes</li><li>Navigate to the table <em>Tbl_Song.&nbsp; </em>This is the table that records all the song entries.</li></ol>



<p class="wp-block-paragraph">Below is an explanation of each column:</p>



<ul class="wp-block-list"><li>Song_ID: numerical identifier for each song (the program lists them as 5 digit numbers starting at 10000)</li><li>Song_Title: song title</li><li>Song_Singer: each singer has a unique number associated with them (see next section)</li><li>Song_Singer (2nd one): the name of the artist in text</li><li>Song_Word: number of characters in the song name</li><li>Song_Type: a number representing a language (Mandarin,Taiwanese,Cantonese,Hakka Chinese,English,Japanese,Movies,Cartoons,Other&nbsp; in that order starting from 1)</li><li>Song_Volume: song volume, but not sure what units they are in.&nbsp; Default value is 70.</li><li>Song_Channel: the audio channel that does <em>not</em> have the vocal track. (1-Left, 2-Right, 3-Both)</li><li>Song_FileName: filename of the video without the directory</li><li>Song_Path: the directory to the file (could use absolute pathing only, but unsure of whether relative paths work)</li><li>Song_Create: the time that the song was added in</li><li>Song_Count: the play count of a song</li><li>Song_Juyin: the Zhuyin characters representing the song title</li><li>Song_Stroke: number of strokes in the first character of the song name</li></ul>



<p class="wp-block-paragraph">Some of the columns can be left out, but that means that it will not be possible to find a particular song using the omitted information.&nbsp; For example, Song_Juyin can be left out for those who dont use the Zhuyin system, and that feature won’t be used for song searching anyway.</p>



<p class="wp-block-paragraph">To add a song, fill out the following information at the minimum on one row:</p>



<ul class="wp-block-list"><li>Song_ID (must be a unique number and should have five digits)</li><li>Song_Title</li><li>Song_Singer</li><li>Song_Volume (70 is the default)</li><li>Song_Channel</li><li>Song_FileName</li><li>Song_Path</li></ul>



<p class="wp-block-paragraph">For the Song_Singer information, refer to the next section.</p>



<h3 class="wp-block-heading"><a name="insertartist"></a>Inserting A New Artist</h3>



<p class="wp-block-paragraph">Artist information is stored on a different table called <em>Tbl_Singer</em></p>



<ol class="wp-block-list"><li>Open the table called <em>Tbl_Singer</em></li><li>Fill out an entire row to add a new singer (see below for the reference for each information column)</li></ol>



<p class="wp-block-paragraph">Below is an explanation of each column:</p>



<ul class="wp-block-list"><li>Singer_ID: unique identifier for each singer (this is the unique ID that is to put inserted in the Song_Singer column in <em>Tbl_Song</em>)</li><li>Singer_Sex: singer gender (0-Female, 1-Male, 2-Group/Band)</li><li>Singer_Name: artist name in text</li><li>Singer_Juyin: the Zhuyin characters representing the artist name</li><li>Singer_Stroke: number of strokes in the first character of the artist’s name</li></ul>



<h1 class="wp-block-heading"><a name="testing"></a>Testing</h1>



<p class="wp-block-paragraph">One a song or two has been entered into the database, one can test it by opening up the JetKTV program and trying to pick a song.&nbsp; It is working when one screen shows the video playing and another screen showing the JetKTV GUI.</p>



<p class="wp-block-paragraph">One can also try clicking the button labeled &#8220;導唱&#8221; to test if the audio channels are set up properly (toggling it turns on and off the vocals).</p>



<p class="wp-block-paragraph">The next step would be to plug in the computer with all the television components and then trying it again.&nbsp; Once everything works, the system is ready.</p>



<h1 class="wp-block-heading"><a name="ref"></a>References</h1>



<p class="wp-block-paragraph">All reference sites are in Chinese</p>



<p class="wp-block-paragraph">[1] 動手打造窮人 KTV <strong>&lt;</strong>http://www.jetktv.ktvdiy.com/&gt; [Update 2022 October: JetKTV seems to be defunct]</p>



<p class="wp-block-paragraph">[2] [影音相關] JetKTV 輕鬆打造免費 KTV 點唱機 (進階設定篇) &lt;<a href="http://www.soft4fun.net/video-related/%E5%BD%B1%E9%9F%B3%E7%9B%B8%E9%97%9C-jetktv-%E8%BC%95%E9%AC%86%E6%89%93%E9%80%A0%E5%85%8D%E8%B2%BB-ktv-%E9%BB%9E%E5%94%B1%E6%A9%9F-%E9%80%B2%E9%9A%8E%E8%A8%AD%E5%AE%9A%E7%AF%87.htm#doublescr" target="_blank" rel="noreferrer noopener">http://www.soft4fun.net/video-related/%E5%BD%B1%E9%9F%B3%E7%9B%B8%E9%97%9C-jetktv-%E8%BC%95%E9%AC%86%E6%89%93%E9%80%A0%E5%85%8D%E8%B2%BB-ktv-%E9%BB%9E%E5%94%B1%E6%A9%9F-%E9%80%B2%E9%9A%8E%E8%A8%AD%E5%AE%9A%E7%AF%87.htm#doublescr</a>></p>



<p class="wp-block-paragraph">[3] 峰網誌 JetKTV-DIY電腦點歌機..軟體篇 &lt;<a href="http://www.wretch.cc/blog/Linpy/4853370" target="_blank" rel="noreferrer noopener">http://www.wretch.cc/blog/Linpy/4853370</a>></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.henrypoon.com/blog/2012/01/02/setting-up-karaoke-ktv-like-an-actual-karaoke-place-at-home-using-jetktv/feed/</wfw:commentRss>
			<slash:comments>46</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1301</post-id>	</item>
		<item>
		<title>Freiburg im Breisgau</title>
		<link>https://blog.henrypoon.com/blog/2011/02/21/freiburg-im-breisgau/</link>
					<comments>https://blog.henrypoon.com/blog/2011/02/21/freiburg-im-breisgau/#respond</comments>
		
		<dc:creator><![CDATA[hp]]></dc:creator>
		<pubDate>Mon, 21 Feb 2011 20:31:29 +0000</pubDate>
				<category><![CDATA[europe]]></category>
		<category><![CDATA[Animals]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[Bebenhausen Abbey]]></category>
		<category><![CDATA[Berlin]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[Bratwurst]]></category>
		<category><![CDATA[Castle]]></category>
		<category><![CDATA[Chan]]></category>
		<category><![CDATA[Chinese people]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[D]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Dia]]></category>
		<category><![CDATA[dog]]></category>
		<category><![CDATA[EAR]]></category>
		<category><![CDATA[Euro]]></category>
		<category><![CDATA[Europe]]></category>
		<category><![CDATA[Family]]></category>
		<category><![CDATA[Fiction]]></category>
		<category><![CDATA[Fictional characters]]></category>
		<category><![CDATA[Final]]></category>
		<category><![CDATA[Find]]></category>
		<category><![CDATA[Games]]></category>
		<category><![CDATA[German]]></category>
		<category><![CDATA[Germany]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[God]]></category>
		<category><![CDATA[Gun]]></category>
		<category><![CDATA[Heidelberg]]></category>
		<category><![CDATA[Hiking]]></category>
		<category><![CDATA[Hole]]></category>
		<category><![CDATA[Human]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[IME]]></category>
		<category><![CDATA[In Germany]]></category>
		<category><![CDATA[IPod]]></category>
		<category><![CDATA[ise]]></category>
		<category><![CDATA[It]]></category>
		<category><![CDATA[Jesus]]></category>
		<category><![CDATA[Jesus Christ]]></category>
		<category><![CDATA[Karlsruhe]]></category>
		<category><![CDATA[Lie]]></category>
		<category><![CDATA[Light]]></category>
		<category><![CDATA[Mech]]></category>
		<category><![CDATA[Mecha]]></category>
		<category><![CDATA[Mind]]></category>
		<category><![CDATA[Nap]]></category>
		<category><![CDATA[Outside]]></category>
		<category><![CDATA[Palpatine]]></category>
		<category><![CDATA[Pho]]></category>
		<category><![CDATA[pokemon]]></category>
		<category><![CDATA[Port]]></category>
		<category><![CDATA[rain]]></category>
		<category><![CDATA[RAR]]></category>
		<category><![CDATA[Run]]></category>
		<category><![CDATA[Sausage]]></category>
		<category><![CDATA[Schloss]]></category>
		<category><![CDATA[Snow]]></category>
		<category><![CDATA[Stairs]]></category>
		<category><![CDATA[star wars]]></category>
		<category><![CDATA[STR]]></category>
		<category><![CDATA[Struct]]></category>
		<category><![CDATA[Stuttgart]]></category>
		<category><![CDATA[Thou]]></category>
		<category><![CDATA[Time]]></category>
		<category><![CDATA[Trac]]></category>
		<category><![CDATA[Transport]]></category>
		<category><![CDATA[transportation]]></category>
		<category><![CDATA[Travel]]></category>
		<category><![CDATA[tuna]]></category>
		<category><![CDATA[UNC]]></category>
		<category><![CDATA[Urge]]></category>
		<category><![CDATA[Week]]></category>
		<category><![CDATA[Wiki]]></category>
		<category><![CDATA[Wikipedia]]></category>
		<category><![CDATA[Woke]]></category>
		<category><![CDATA[Words]]></category>
		<category><![CDATA[Writing]]></category>
		<category><![CDATA[Z]]></category>
		<category><![CDATA[Zip]]></category>
		<guid isPermaLink="false">http://henrypoon.mooo.com/blog/freiburg-im-breisgau</guid>

					<description><![CDATA[Day 40 Now that I’m not occupied by episode after episode of TV shows, I can finally catch up on my blog rather than keeping a queue of all the things I want to blog about. In my attempt to go somewhere new every weekend, I chose to go to Freiburg im Breisgau last Saturday. [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Day 40</p>



<p class="wp-block-paragraph">Now that I’m not occupied by episode after episode of TV shows, I can finally catch up on my blog rather than keeping a queue of all the things I want to blog about. In my attempt to go somewhere new every weekend, I chose to go to Freiburg im Breisgau last Saturday.</p>



<p class="wp-block-paragraph">I woke up early in the morning at around 5 and got ready to leave the house at 6:30.&nbsp; I probably woke up my landlord’s family downstairs from the running water while I showered, but I unfortunately, I can’t really do anything about that.&nbsp; I figured leaving an hour early to meet up with my friend at a train station that only takes half an hour to get to should give me quite a bit of flex time. Half an hour seemed like a lot to me, until I saw how long I had to wait for my train.&nbsp; I waited a whole twenty minutes for it.&nbsp; The worst part about the wait was that I never charged my iPod.&nbsp; I stood around and silently waited.&nbsp; Kind of like watching grass grow.&nbsp; Although in retrospect, I could have left the house a little later, but at least I wouldn’t miss my train.</p>



<p class="wp-block-paragraph">After meeting up with my friend, I we patiently waited for the train.&nbsp; From the corner of my eye, I saw something tiny moving around on the tracks.&nbsp; An animal?&nbsp; It was a mouse, or a rat.&nbsp; I don’t know the difference.&nbsp; I tried my best to catch a picture of it while it zipped around and underneath the train tracks.&nbsp; It kind of felt like playing that old N64 game, Pokemon Snap.&nbsp; If this was Pokemon Snap, I would have gotten a low score for that picture.</p>



<p class="wp-block-paragraph">The train arrived at around 7:30 and it would stop at Karlsruhe.&nbsp; On the ride there, my friend and I just talked while looking looking out the window.&nbsp; After we arrived in Karlsruhe, we had to wait a half hour for our connecting train.&nbsp; The moment I looked out onto the station platforms after stepping out of the train, the sight instantly reminded me of the City 17 train station in Half-Life 2 (everything reminds me of games doesn’t it?)</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXHDapBawrsDqiz7eDguVUnnUE9QYtI6ni3dmf85SbnKdy6eIzIC1Gw_VuIpDjiiRwwn7Gc2nUaSRUddslxytNk-07BcGN3w5KiLx9rAyi4ZMA3KFSlKKKIua3zHAIVUIiUV2EVJUpj4wexyxJUnk2Zkg=w1226-h919-no" alt=""/><figcaption>Maybe not so much after looking at the picture a little more…</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://i0.wp.com/images1.wikia.nocookie.net/__cb20090530003702/half-life/en/images/thumb/5/5a/Scanner_welcome.jpg/800px-Scanner_welcome.jpg?w=1280" alt=""/></figure>
</div></div>
</div>



<p class="wp-block-paragraph">While walking around the train station trying to kill time, we found a place that sold 5 Berliners for 2 euro.&nbsp; Although called a Berliner, it clearly has nothing to do with someone from Berlin.&nbsp; Despite the misleading name, Berliners are a pastry that is like a European version of a doughnut (never ever call it that).&nbsp; It’s got sugar on top and jam filling on the inside.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="http://upload.wikimedia.org/wikipedia/commons/4/4c/Berliner-Pfannkuchen.jpg" alt=""/><figcaption>&#8220;Ich bin ein Berliner&#8221; &#8211; John F. Kennedy</figcaption></figure>



<p class="wp-block-paragraph">After boarding the connecting train, I talked to my friend about how long train rides were.&nbsp; I don’t know if it’s because English attracts attention, but a random stranger sitting across from us started talking saying how it is perfectly normal for train rides to take a long time.&nbsp; Since he was German and probably didn’t get a lot of practice with English, he spoke to us with a heavy accent.&nbsp; He started talking about how sometimes people will commit suicide by train, which causes trains to stop in the middle of nowhere and everyone on board is forbidden to get out.&nbsp; The weirdest part of it was that train suicides were <em>seasonal</em>.&nbsp; According to him, more people commit suicide by train in the summer.&nbsp; After speaking to him a little more, I found that even some Germans read manga.&nbsp; I didn’t know that manga was that popular.&nbsp; I even saw Dragon Ball manga translated in German in a bookstore.</p>



<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN3ibqeAoG9glq0c8aXpYssoKQjzTJb0JcC-vQcWSR0oY65RVaxuXdWNKfo_l7I4Q/photo/AF1QipOZZL1Kubr_92JokZ9wPs_TZ-V7dUpZJlFPlsLa?key=SUFwSDB1OHV0V3R4ZFE2QUI4c2gtUmRiQ1hZYkt3" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXm1bUEEUmauvN9ZUxiBNPftNa_283936X_VtihYPiynmbMPfoLd2KAAIto-6jkBwX03H0Yp9Q-dX47hiNZZhSdzXlvfrjvI1Yx0yMR89EGhdO3REedT5uMjFrx-Gpe9ZOJHIfrmcz9BlVCnJZnFNFRXQ=w1186-h889-no?authuser=0" alt=""/></a><figcaption>Ich bin ein Super Saiyan?</figcaption></figure>



<p class="wp-block-paragraph">Two train transfers and three and a half hours later, we arrived in Freiburg im Breisgau.&nbsp; According to Wikipedia, it’s the warmest place in Germany.&nbsp; As usual, the first thing we do upon arriving at a new place is to look for a map to find the tourist information center to get a map.&nbsp; I had already looked up what sights to see the night before, so all I needed was a map to tell me how to get there.&nbsp; Even without the map, we could both see the tower of the Freiburger Munster standing tall above every building in its vicinity.&nbsp; All we needed to do was head in that direction.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipOz_NpE72ShUW1HVKFndoHKIAfeKFIA1mIIY352?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXbwOk08h5jXcUZeqe_qbcEEFpQY_oHtH8Xv4a53H7Q5QUFzyhEvFEzNGxdUZDLlVtqx7w_KnfYEh9cVlBIPGxC8aUnczR_HQx49XOrd_0STuzBgrpwGj5sxmg4Mz8O4E1ZP4AxcCTOQE11QUSnEYE0zQ=w690-h919-no" alt=""/></a><figcaption>The Cathedral</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipMKlrNpK9BFmMUxqKE5yBqpG1HUkujydbH5hXpc?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEUxzGLqE6ukUtePsZ2lWmG_ipuWorYa0OhRuOaO9LhCVd_Ws4EEsgd5dJoBQ4QVqqZwlTzEgD4tOCsycv5L_lOp-Z6PiQDoIDCCc-5EdBZ_nKxUCWGr3ExYOxO1QBe59FSjZGeUgmQtNRT1TqC3hjr9Vw=w690-h919-no" alt=""/></a></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipNz8NG2WrGAIKK_jH3cia-bE8STGvgHehQL44TQ?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEUeEf6QElTZYgbh5S-aoQuAiEWrwsgQzMSPUjXpYi2pOA6MWIzD0wKBHNLkqpYn5RT52TEk3sxEBciRDYbJ01fy5BJewuHjTPIWWzggsEoRuYncJJ5nzIRJf3Qeg_OJsZ-STx_kP3KjnXKGsJHPPRlW7A=w1226-h919-no" alt=""/></a><figcaption>Munsterplatz</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipPzIzgbRyrpnxAFiBCt3FswmhIv23IbNA0eeMAa?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEWb1xr0XdQEoOEFh-G5af5cbtog0P4VN21hgl7z6R9d3VobvB6qHEOeEJ1JuTTj71-MPdGtQC_PZwWZwKW-MwHclydvSoFZMU07WKzO5NLn_fnXQ6yfxHdXEfy9Ve8_DV9sVzEAV9zo-1TZIDEW64xK-g=w1226-h919-no" alt=""/></a></figure>
</div>
</div>



<p class="wp-block-paragraph">Thus far, the Freiburger Munster is the most amazing work of church architecture I have seen, inside and outside (The only place that I think could be better is the Sistine Chapel, but I haven’t been there yet). I would describe it, but I’m terrible at descriptive writing.&nbsp; Good thing is that a picture is worth a thousand words!</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipO2kxLMGtirsQt0U05lafuBPW3RCXwd3RY0Du0w?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVQzn4pFp8uD1nL7lTI-e10Ab3taoDkyxhp2dJJZ1B70q6S5QIROUzCQ3zlIUC7v5swkfQf7ag0BIN3Rb9FD9zRMNOh0fwU6x1Kri1N1WPwyNu3Ezn544xC0PlnkcoQVoQ75Uqw7AQxHxgKmhcIiK1_Sg=w1226-h919-no" alt=""/></a><figcaption>The Cathedral Entrance</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipMWv0K2FnXezYYwFrrI3G835TeAlyr3Y9KhatgZ?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVuAu1c9zWn_8hk1wRvcF88BUeZ_mwwLM3tjrNy9NIRHw6MACGck3JJAiUGFDZdxXIorcC-uyTbSUy9Es0EfiJOha3EjucGR5U9W8DGEOoDDljAoXSF527OvxZ0opLDdOj8UkIRAKa3b8ZlkqqEhLuwgQ=w1226-h919-no" alt=""/></a></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipM7pGZ4dV9TxI1z4ro2SiNRWX9XCCj2bgmqOB_w?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEUCdWARs9XacZlMySmmifwkDxhCBSduGvY9og6_OlRWdoI6Y2G-yGQuBZxnBHrEKSnRyD06Y9Z38nZQ4T5vvNn4I9bfUkkkIJcgSkgRV4fQawNyqlDqNZPaifO6c_D8lo_wlI_RdCYSDTMSYe4IoSvJrw=w1226-h919-no" alt=""/></a></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipM-whiBvxs0dqSFp-gBAZkh-NGktatk5_KqGgvf?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVvWSUhx2TxAn1Y0yG4Xku1hvanehTbW96HEQW3DLKyefnJhx544iGy-5IgZihATnVk1s7C2ZcWg4moPRORWRJO56TvOsEnimA66qAh0RQpV4wyXiC_QbvXDTsTjhxr-vd1Ao5Sd-P0MouYrgSEbloQuQ=w1226-h919-no" alt=""/></a></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipMx6-2lozs2Zj6VkOOTv5p1Bjs4a5gnoEYxAfdb?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXj_eMLKt4QZSqebP1lD5IXVKc3sXPOU-5hIJtts5zUBP0KBtRPLyqUK01pMVGRSt4uHe9r2YNPenh7CGB55SRmD0j5eKm9eR8mKnke07_ldFV8cR1zmc8DEa5F7JDANgxVXxEXvr2z3XPoN6IbPwon5Q=w1226-h919-no" alt=""/></a><figcaption>The Interior</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipNQnirLvKL5XryCd74CVgNpsrMJX_DKmJX69LgV?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVG-0CVr88GcrRIBVLz2cUEMeYkhoO3ldzwskNopQtbUSJdGAaK-SkGFi89UG2ltGjJut_oG_r5mfjH33gm4QcaBVrsSRaj6yuTJf-HJQvK8eCc3Eo9R28txVyeb_qPOHicA6Yml3hJDdMOgIbt_L4ozw=w1226-h919-no" alt=""/></a></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipN9GKNK6ctni359Irl0-qmA6OLylkTZp04hoWaK?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEWw5pPUaXlD-H18B0V2b5cfbvd60VuXBZ1JwDNcDPPhlsMTlokgJbfLQ5PUGoTfxyg-0Uxq6PwgxOV7ZNED1Ub4SdaIeE3RtOauM_BfV-ToTl8tpeCmf4t5XUpzpCSoDaDcU5BM3J8_x5tb34RlNiafaA=w690-h919-no" alt=""/></a><figcaption>Although a bit blurry, here’s my obligatory picture from the entrance</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipPm-21xjb6CINhtZ17b4mVczbm_T4KLQJ4d67mV?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVaThZYeea2H5XftSNconZLk0pPFi4QslqVwyWxsfRovPzV5QeCi01x5qIi-9qRWmRTYuwLUAouCPs7U7Aivb78u98GqqxVtBASgQd6D2rglBWaUE1gpia5zM9ePjvI3RP4nbgAhqU77CJuV3pwJdWh3w=w690-h919-no" alt=""/></a><figcaption>I always enjoyed seeing candles lit up like that</figcaption></figure>
</div>
</div>



<p class="wp-block-paragraph">After walking around inside for a little bit, I came across a set of statues in front of a window set up in a way that a group of statues were all looking at the body of Jesus Christ.&nbsp; When I was looking at these statues, it happened to be around noon, and the sunlight shined through the window and I could see the glare.&nbsp; It seems as if the scene was designed this way: to have the Light of God shine down.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipNODYMrrDvxg8wc1sPX9EQQfDBuBVCTPn1GF9pe?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVGIPcCTZXvOV5i6XlMdctLzAJVg8aupJO2K8vTLO5VwQLC5-7ZYjIJTk-p2K-FFDHU5zA8NpLNRz5QrnAR-FxLV_1JqOr8M3yKNEqcxEQYhdHsFOrMS18qxYMYEGvn4X7T2jJggsysME-FmQaW4MfXJQ=w690-h919-no" alt=""/></a><figcaption>The Light of God</figcaption></figure>
</div>


<p class="wp-block-paragraph">Walking around some more, I came across a statue that looked suspiciously like Chancellor Palpatine of Star Wars (there’s my nerdiness popping up again).&nbsp; Who knows, maybe George Lucas got inspiration from this and found an actor that looks like the guy in this statue.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipOQFczEFIrWXL6CyX0QTQTKoMSWCWwQGVoAxoU_?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEWuT1CaWdCboMFRjgT-XL05ps0VNDuvm19b-k5FThdFUD19QFAjvz6GtzmAjPZ7owRtn1dMNdfARxf8S-ufjIbpS_5iCHC_NjFcbK4ibPLWVHT8o6JlqiHK-Cff2UvLuhTMeHPWZ9cJVBziKOTtb9BMxA=w690-h919-no" alt=""/></a><figcaption>Chancellor Palpatine?</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"><div class="wp-block-image">
<figure class="aligncenter size-large"><a href="http://starwars.wikia.com/wiki/Palpatine" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://i0.wp.com/images1.wikia.nocookie.net/__cb20060623225923/starwars/images/thumb/b/be/Reassuring_Smile.jpg/199px-Reassuring_Smile.jpg?w=1280" alt=""/></a></figure>
</div></div>
</div>



<p class="wp-block-paragraph">On our way out, we learned that we could visit the church tower.&nbsp; Despite the popularity of the church tower, there was no elevator (makes sense, since the church is <em>quite</em> old) and had a set of the narrowest spiral staircases ever seen.&nbsp; Two people could barely pass through each other.&nbsp; One wrong step would cause one to tumble around and around down to the bottom and bring other people along in a human snowball.&nbsp; Luckily for me, that did not happen, but it sure was tiring walking up all those steps.&nbsp; At the top, we had a semi decent view of the town.&nbsp; I say semi-decent because of the maintenance that was going on.&nbsp; For three weeks in a row, every main attraction in each city I went to was under maintenance.&nbsp; There was the Heidelberg Castle, Bebenhausen Abbey, and now this.&nbsp; In addition to being able to look around town, I also saw the mechanism for the church bells.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipOx60sdR7WYPcyxkJ12XyMXCk8Hl5UQ1n_BLjII?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXSHwofcAiOCIp7JIUbFfnQMlWUm9e9ipndH4iG6sQT0MsCrrJy3N2I05Pd8F6KDQZ37KKQyH5St_qEE_BtqNVJsYOydzKyq6bk4zNhD9tBkEXSOQpcJS0JxTaHIrSoqD8274cd4Y286-2gYCDUHTxdLg=w1226-h919-no" alt=""/></a><figcaption>Munsterplatz from the Tower</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipOXuzABzqhUiRlbV70eoRsPOglxoA8egtHHV6zK?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXRvgLua--vKBEyTtjXM-GhYWUQhMJyY4a8cVz4dVMRfaw_QBEWmIO3mzVGdMGNCFUoExALy99EbsFC6A2oLK1pbPmuTFtFlrA86vKNBmsT9KoWA9RWSnBAgwARSNzQaTGsIAWffH8Fj0KMQyMxkK7dtw=w1226-h919-no" alt=""/></a></figure>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipMPmQnsagpeVzIXz2-lTHMpc2NqP-LD0YywNXRF?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEUHGvj9-DvWK97VykSA9_cwIjD16ZU4OtOQXCGNfS1tsfQ9-e1H_vBHKtoaxGk5oP1MYBtmFIXkDGM_tWnHoIpFI6SfYF_YjYmRF6OHYkO7h7zBtOvF9LSGbFBqrxKHIyRVIPUD9o9xaoFQDhgPaHxiVA=w1226-h919-no" alt=""/></a><figcaption>Church Bells</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipMoLvQBYHyav69qUPG8UVTEcaBmCYh-M4m5ROJb?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXkeyWyjpyf4hVN77gfGe1e5P3gBSIfvWcTSMDUusVBqo9GQPPVoZO673oiAb0gIX3fMHhiy4mdSkjouMRkJb28fJ19Bbqc5S9fObSv1dmmPgZvxLU7gXXBDo3Vu7qINYYByN9i839eGst-kSNtsrZ7YQ=w1226-h919-no" alt=""/></a></figure>
</div>
</div>



<p class="wp-block-paragraph">On our way out of the cathedral, we smelled a strong scent of bratwurst and because of that, I decided that’s what i was going to have for lunch.&nbsp; I guess my lunch was like a European hotdog (I have no idea what they call it).&nbsp; They took a bread roll, cut it in half and put bratwurst and onions inside.&nbsp; Like everyone else, I added ketchup and mustard.&nbsp; I don’t think I can go back to eating regular North American sausages again after eating bratwurst.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><a href="http://en.wikipedia.org/wiki/Bratwurst" target="_blank" rel="noreferrer noopener"><img decoding="async" src="http://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Drei_im_Weggla.jpg/609px-Drei_im_Weggla.jpg" alt=""/></a><figcaption>This picture is from Wiki, but this was essentially what it was</figcaption></figure>
</div>


<p class="wp-block-paragraph">A unique feature of the city is that water canals run through the town.&nbsp; Upon first sight, one would think they were some sort of sewage system.&nbsp; Known as <i>Bachle</i>, they were once used to provide water for firefighting and feed farm animals.&nbsp; It is said that whoever steps in one must marry a Freiburger.&nbsp; Around the town are a set of gates.&nbsp; One of them, the Martinstor, has now been desecrated by McDonald’s.&nbsp; How they managed to put their brand on such a historic structure is beyond me.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipMzUdcDQDRcElUraAdggWo7crVjXl_7u_ounT05?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVQUEWo_OkyEXWlpyrJepk0rxiMWCdlZMUWsuewuCiWDMVRJ9iZNPbSVbadty8OfkoDRQCp69zCqN8ttneEnNF_sSTuOM2gGERd6xuutpspp-DiNM3KTz_DoPJtJbn84aiDgcB0iD8FFlo9CH6B_lXGOA=w1226-h919-no" alt=""/></a><figcaption>The Bachle</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipMTu6F4JiYhX90E9Gav1RXJPMTcPfYTdKSHdstn?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVibWL3N1so_lXMmv5oUydK7IJgasC_aGFQayNoYHM0mP7w15MkUYHLV5YZaGZX4NzGPjwlJTroeFHEFmFJfi6dccV2mJFgPG_yqnrbD6xcS9QSibLv3EZmwtVKnaCBIbNh8HzFPP0UvTZUepM2LqM6Jw=w1226-h919-no" alt=""/></a><figcaption>The Martinstor</figcaption></figure>
</div>
</div>



<p class="wp-block-paragraph">In the city is also a trail that leads up to the mountain, Schlossberg, for a view of the town.&nbsp; I believe there was also public transportation that took the lazy folks up there.&nbsp; As if the stairs at the church bell tower wasn’t even enough, we were now faced with yet another seemingly endless set of stairs.&nbsp; We’d look up the mountain seeing how much further we needed to go every now and then.&nbsp; Once we reached the top, we realized that it wasn’t really the top.&nbsp; There was yet another set of staircases leading up.&nbsp; It’s just that we couldn’t see it before we got all the way up.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipP2W9gkvmalSqj743rd4JQXiANBinc0lEsAPMl4?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXbuQNbXkpqZVlvHR6pBH_mrFM95b39zZqOAXSeYvuXHSnM4MgqZXuZSHhnDgwSc2Zj7-U40GAQ6NjQ4sRIDqt31X8GUs222Oto-Xkmj3WFeZw9pZ_nyfebqhPbvU-nOEJtRRkKm_XKry6zCsuNUcBwXQ=w1226-h919-no" alt=""/></a><figcaption>Layers and Layers</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large is-resized"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipNXk8Qm_Evf7f3n_bhLW90ydAvzmVqWOaAuKL9l?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEXm2HDVUwBaP05uHM4LtSUU0RB5swjXBygWIuOAIvR3UAcMCgyPtgAR7EQh5Y6wWlP1GT0SlbqKwLtijy9WzYnPmWPpshmQr3dqM4cSKTgYoCzOG7PWeqvJv8pPEnoSCGb2ZUhvd3twoqIbDQF8uSR6Mg=w1226-h919-no" alt="" width="1225" height="919"/></a><figcaption>The View from Schlossberg</figcaption></figure>
</div>
</div>



<p class="wp-block-paragraph">On our way home, I came across a vandalized billboard on the side of the street.&nbsp; The vandals had drawn a Hitler mustache on the peoples faces.&nbsp; I found it kind of amusing since that doesn’t really happen in North America.&nbsp; Not Hitler mustaches, anyway.</p>



<figure class="wp-block-image size-large"><a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ/photo/AF1QipOueC6yTBXhGtDuOQqRVbGpulhbYtvn74ixQEiZ?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener"><img decoding="async" src="https://lh3.googleusercontent.com/pw/AL9nZEVV4rj0Y7Xf4B5V2IhlE9k3uG2LoFXfpCGNH7_9sfTk48ByDWHp9YwcWdX5fLqIcVc-AApuoSCdYJDIz6mTuKjNsTEONIr5KFp2GWqgwVk5Ty2VTgQMhKCSlm6q0i9UdVIa1s07HnQhq3ckMC-MZ07MvQ=w1226-h919-no" alt=""/></a><figcaption>Mini Hitlers and an Old Man Hitler?</figcaption></figure>



<p class="wp-block-paragraph">&nbsp;On the train ride home yet another random stranger talked to us.&nbsp; Except that the stranger this time wasn’t German.&nbsp; He was an American who had just arrived in Stuttgart to work.&nbsp; Like us, he spent his weekend traveling.&nbsp; My friend and I were talking about gun control at the time when the guy started talking to us.&nbsp; Apparently in his state of Virginia, one has the right to shoot someone in self defense to take someone’s life when their own is in danger.&nbsp; He even told us a story about how someone he knew shot at a guy sneaking around suspiciously in their yard and how a friend of his has a license to carry a concealed weapon around.&nbsp; It felt kind of nice to hear North American English again.</p>



<p class="wp-block-paragraph">At the train station, while waiting for a connecting train, a third random stranger started talking to me.&nbsp; Three in one day.&nbsp; That has to be a record.&nbsp; This time it wasn’t because I was speaking English.&nbsp; This time it was because I’m Chinese.&nbsp; A Turkish looking fellow looked at me as he walked by pushing a cart of luggage.&nbsp; He looked at me and said “ni hao”, which is hello in Mandarin.&nbsp; I was so surprised that I didn’t know how to reply, all I did was smile back in appreciation.&nbsp; I guess Chinese people are a rare sight around here.&nbsp;</p>



<p class="wp-block-paragraph">For some reason, on the train ride home, the train stopped suddenly in the middle of nowhere.&nbsp; I was reminded by the German fellow I talked to earlier about train suicides and I hoped that it didn’t happen.&nbsp; Fortunately, it wasn’t anything serious and the train continued on its way after a few minutes.&nbsp; After a nice day of sightseeing and hiking around, I got home and rested for the next day, which I spent uploading photos, blogging, and random household chores.&nbsp; Sounds like quite the opposite in excitement compared to the day before.</p>



<p class="wp-block-paragraph">My <a href="https://photos.google.com/share/AF1QipN4_xSqOrEHx7LIorCsORhRytpX6fyrirk2LyxK5qaAi8Sm8h8WOfatmiRU-6xESQ?key=N044blFoYTgyRkZFc2k3RGN5RWFmdUh3S0VJOUdR" target="_blank" rel="noreferrer noopener">photo album</a> for Freiburg im Breisgau</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.henrypoon.com/blog/2011/02/21/freiburg-im-breisgau/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">843</post-id>	</item>
		<item>
		<title>Serial Communication in Java with Example Program</title>
		<link>https://blog.henrypoon.com/blog/2011/01/01/serial-communication-in-java-with-example-program/</link>
					<comments>https://blog.henrypoon.com/blog/2011/01/01/serial-communication-in-java-with-example-program/#comments</comments>
		
		<dc:creator><![CDATA[hp]]></dc:creator>
		<pubDate>Sun, 02 Jan 2011 01:46:37 +0000</pubDate>
				<category><![CDATA[computer stuff]]></category>
		<category><![CDATA[9]]></category>
		<category><![CDATA[All That]]></category>
		<category><![CDATA[APT]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Books]]></category>
		<category><![CDATA[Chan]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[Comment]]></category>
		<category><![CDATA[Computer]]></category>
		<category><![CDATA[Computer languages]]></category>
		<category><![CDATA[Const]]></category>
		<category><![CDATA[Cookbook]]></category>
		<category><![CDATA[D]]></category>
		<category><![CDATA[Data types]]></category>
		<category><![CDATA[Dia]]></category>
		<category><![CDATA[Download]]></category>
		<category><![CDATA[Drive]]></category>
		<category><![CDATA[EAR]]></category>
		<category><![CDATA[Event]]></category>
		<category><![CDATA[Events]]></category>
		<category><![CDATA[Final]]></category>
		<category><![CDATA[Find]]></category>
		<category><![CDATA[Flag]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Hole]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[IDE]]></category>
		<category><![CDATA[IME]]></category>
		<category><![CDATA[Introduction]]></category>
		<category><![CDATA[IOS]]></category>
		<category><![CDATA[ise]]></category>
		<category><![CDATA[It]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Lag]]></category>
		<category><![CDATA[Lie]]></category>
		<category><![CDATA[Link]]></category>
		<category><![CDATA[Object]]></category>
		<category><![CDATA[Object-oriented programming languages]]></category>
		<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[Over]]></category>
		<category><![CDATA[Page]]></category>
		<category><![CDATA[Parameter]]></category>
		<category><![CDATA[PDF]]></category>
		<category><![CDATA[Plan]]></category>
		<category><![CDATA[Port]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Programming languages]]></category>
		<category><![CDATA[RAR]]></category>
		<category><![CDATA[Reference]]></category>
		<category><![CDATA[Rolling]]></category>
		<category><![CDATA[Run]]></category>
		<category><![CDATA[Serial]]></category>
		<category><![CDATA[Serial communication]]></category>
		<category><![CDATA[Software design patterns]]></category>
		<category><![CDATA[Software engineering]]></category>
		<category><![CDATA[Speed]]></category>
		<category><![CDATA[STR]]></category>
		<category><![CDATA[String]]></category>
		<category><![CDATA[Struct]]></category>
		<category><![CDATA[Swing]]></category>
		<category><![CDATA[Thou]]></category>
		<category><![CDATA[Time]]></category>
		<category><![CDATA[UNC]]></category>
		<category><![CDATA[URL]]></category>
		<category><![CDATA[Variable]]></category>
		<category><![CDATA[Vol]]></category>
		<category><![CDATA[Wiki]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress.com]]></category>
		<category><![CDATA[Writing]]></category>
		<category><![CDATA[XBee]]></category>
		<category><![CDATA[Z]]></category>
		<category><![CDATA[Zip]]></category>
		<guid isPermaLink="false">http://henrypoon.mooo.com/blog/serial-communication-in-java-with-example-program</guid>

					<description><![CDATA[This is more of a follow-up to my previous post about serial programming in Java (here) and how to install the RXTX libraries (here).&#160; This post also assumes that Java is already properly set up with RXTX. Generally, communication with serial ports involves these steps (in no particular order): Searching for serial ports Connecting to [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This is more of a follow-up to my previous post about serial programming in Java (<a title="Java Serial&nbsp;Programming" href="http://henrypoon.wordpress.com/2010/10/11/java-serial-programming/" target="_blank" rel="noopener">here</a>) and how to install the RXTX libraries (<a title="Installing RXTX for Serial Communication with&nbsp;Java" href="http://henrypoon.wordpress.com/2010/12/25/installing-rxtx-for-serial-communication-with-java/" target="_blank" rel="noopener">here</a>).&nbsp; This post also assumes that Java is already properly set up with RXTX.</p>



<p class="wp-block-paragraph">Generally, communication with serial ports involves these steps (in no particular order):</p>



<ul class="wp-block-list"><li>Searching for serial ports</li><li>Connecting to the serial port</li><li>Starting the input output streams</li><li>Adding an event listener to listen for incoming data</li><li>Disconnecting from the serial port</li><li>Sending Data</li><li>Receiving Data</li></ul>



<p class="wp-block-paragraph">I wrote an example program that includes all of those steps in it and are each in their own separate method within the class, but first I will go through my hardware set up.</p>



<h1 class="wp-block-heading">Hardware Setup</h1>



<p class="wp-block-paragraph">My current hardware setup is as follows:</p>



<ul class="wp-block-list"><li>PC connected to an XBee</li><li>Arduino connected to an XBee</li></ul>



<p class="wp-block-paragraph">User input is given from the PC through the a Java GUI that contains code for serial communication, which is the code presented here.</p>



<p class="wp-block-paragraph">The Arduino is responsible for reading this data.&nbsp; This set up is pretty much using my computer as a remote control for whatever device is on the Arduino end.&nbsp; It could be a motor control, on-off switch, etc.</p>



<h1 class="wp-block-heading">Existing Code</h1>



<p class="wp-block-paragraph">The purpose of this post is to discuss serial programming in Java, and not GUI’s.&nbsp; However, I did create a GUI for testing purposes.&nbsp; See the Code Downloads section for the actual files.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img data-recalc-dims="1" decoding="async" width="300" height="136" src="https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2011/03/javagui.png?resize=300%2C136&#038;ssl=1" alt="" class="wp-image-933" srcset="https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2011/03/javagui.png?resize=300%2C136&amp;ssl=1 300w, https://i0.wp.com/blog.henrypoon.com/wp-content/uploads/2011/03/javagui.png?w=640&amp;ssl=1 640w" sizes="(max-width: 300px) 100vw, 300px" /></figure>
</div>


<p class="wp-block-paragraph">Above is the picture of the GUI complete with the buttons that I use to interact with the program.&nbsp; I also added key bindings which I can use to control the throttle.</p>



<p class="wp-block-paragraph">When the program is first started, none of the GUI elements will work except for the combo box and the connect button.&nbsp; Once a successful connection is made the controls are enabled.&nbsp; This is done through the use of the <em>setConnected(true)</em> and the <em>toggleControls()</em> methods shown in the example code that follows.</p>



<h1 class="wp-block-heading">Imports</h1>



<p class="wp-block-paragraph">The imports i used for this program were as follows:</p>



<pre class="wp-block-code"><code lang="java" class="language-java">import gnu.io.*;
import java.awt.Color;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.TooManyListenersException;</code></pre>



<p class="wp-block-paragraph">Depending on the Java IDE it might already know to tell you to use these imports except for the first one.&nbsp; That first import is specific to RXTX, and all its library methods/classes are in there.</p>



<h1 class="wp-block-heading">Class Declaration</h1>



<p class="wp-block-paragraph">The code here reads:</p>



<pre class="wp-block-code"><code lang="java" class="language-java">public class Communicator implements SerialPortEventListener</code></pre>



<p class="wp-block-paragraph">I named my class <em>Communicator</em>, but the name is really up to the programmer.&nbsp; The name pretty much reflects its intended use.</p>



<p class="wp-block-paragraph">The class should also <em>implement</em> the <em>SerialPortEventListener</em> class.&nbsp; This is a class in RXTX and is required in order to receive incoming data.</p>



<p class="wp-block-paragraph">On some IDE’s this may generate a public void method called <em>serialEvent()</em>.&nbsp; This method will be defined later.</p>



<h1 class="wp-block-heading">Class Variables and Constants</h1>



<p class="wp-block-paragraph">Below are the variables and constants that I defined in my class.&nbsp; What the variables are for is in the comments but a more detailed explanation will follow.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">    //passed from main GUI
    GUI window = null;

    //for containing the ports that will be found
    private Enumeration ports = null;
    //map the port names to CommPortIdentifiers
    private HashMap portMap = new HashMap();

    //this is the object that contains the opened port
    private CommPortIdentifier selectedPortIdentifier = null;</code></pre>



<p class="wp-block-paragraph">I could have easily put the variable definitions in the constructor but it wouldn’t change anything.</p>



<p class="wp-block-paragraph">The <em>GUI</em> object is another class that I wrote separate from this one that contains all the GUI elements.&nbsp; The GUI class extends <em>javax.swing.JFrame</em>.</p>



<p class="wp-block-paragraph">The <em>HashMap</em> is for mapping each of the ports’ names to the actual object. What that means is that I can associate (<em>put()</em> method) the name of a serial port, say a string that says COM1, to an object in the code.&nbsp; Later, I can access the name COM1 from the <em>HashMap</em> by using the <em>get()</em> method and it will return the object that it was associated with previously.</p>



<p class="wp-block-paragraph">The <em>CommPortIdentifiers</em> object is needed to gather the list of ports that are available for connection. by using its <em>getPortIdentifiers()</em> method.</p>



<p class="wp-block-paragraph">The <em>SerialPort</em> object is for storing the data for the port once a successful connection is made.</p>



<p class="wp-block-paragraph">The <em>InputStream</em> and <em>OutputStream</em> is the object that is required for sending and receiving data.</p>



<p class="wp-block-paragraph">The Boolean variable <em>bConnected</em> is just a flag that I use for enabling and disabling elements on the GUI.</p>



<p class="wp-block-paragraph">The constant <em>TIMEOUT</em> is a number required when opening the port so that it knows how long to try for before stopping.</p>



<p class="wp-block-paragraph">The constants for the ASCII values are some values that I use to send through the output stream that act as delimiters for data.</p>



<p class="wp-block-paragraph">The string <em>logText</em> is basically what the comment says.&nbsp; When stuff happens in the program, the program stores a string in this variable and it will be appended to a text area in the GUI.</p>



<h1 class="wp-block-heading">Searching for Available Serial Ports</h1>



<p class="wp-block-paragraph">The method below is for searching for available serial ports on the computer.&nbsp; Code adapted from Discovering Available Comm Ports from the Reference Material.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">    //search for all the serial ports
    //pre style="font-size: 11px;": none
    //post: adds all the found ports to a combo box on the GUI
    void searchForPorts()
    {
        ports = CommPortIdentifier.getPortIdentifiers();

        &lt;span style="color: blue;"&gt;while (ports.hasMoreElements())
        {
            CommPortIdentifier curPort = (CommPortIdentifier)ports.nextElement();

            //get only serial ports
            &lt;span style="color: blue;"&gt;if (curPort.getPortType() == CommPortIdentifier.PORT_SERIAL)
            {
                window.cboxPorts.addItem(curPort.getName());
                portMap.put(curPort.getName(), curPort);
            }
        }
    }</code></pre>



<p class="wp-block-paragraph">The method <em>getPortIdentifiers()</em> returns an Enumeration of all the comm ports on the computer.&nbsp; The code can iterate through each element inside the Enumeration and determine whether or not it is a serial port.&nbsp; The method <em>getPortType()</em> can identify what kind of port it is.&nbsp; If it is a serial port, then the code will add its name to a combo box in the GUI (so that users can pick what port to connect to).&nbsp; The serial port that is found should also be mapped to the HashMap so we can identify the object later.&nbsp; This is helpful because the names listed in the combo box are the actual names of the object (COM1, COM2, etc), and so we can use these names to identify the actual object they are tied to.</p>



<h1 class="wp-block-heading">Connecting to the Serial Port</h1>



<p class="wp-block-paragraph">The method below is for connecting to the serial port once they have been found (see previous section).&nbsp; See How to Open A Serial Port in the Reference Material for more information.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">
    //connect to the selected port in the combo box
    //ports are already found by using the searchForPorts
    //method
    //post: the connected comm port is stored in commPort, otherwise,
    //an exception is generated
    public void connect()
    {
        String selectedPort = (String)window.cboxPorts.getSelectedItem();
        selectedPortIdentifier = (CommPortIdentifier)portMap.get(selectedPort);

        CommPort commPort = null;

        try
        {
            //the method below returns an object of type CommPort
            commPort = selectedPortIdentifier.open("TigerControlPanel", TIMEOUT);
            //the CommPort object can be casted to a SerialPort object
            serialPort = (SerialPort)commPort;

            //for controlling GUI elements
            setConnected(true);

            //logging
            logText = selectedPort + " opened successfully.";
            window.txtLog.setForeground(Color.black);
            window.txtLog.append(logText + "n");

            //CODE ON SETTING BAUD RATE ETC OMITTED
            //XBEE PAIR ASSUMED TO HAVE SAME SETTINGS ALREADY

            //enables the controls on the GUI if a successful connection is made
            window.keybindingController.toggleControls();
        }
        catch (PortInUseException e)
        {
            logText = selectedPort + " is in use. (" + e.toString() + ")";

            window.txtLog.setForeground(Color.RED);
            window.txtLog.append(logText + "n");
        }
        catch (Exception e)
        {
            logText = "Failed to open " + selectedPort + "(" + e.toString() + ")";
            window.txtLog.append(logText + "n");
            window.txtLog.setForeground(Color.RED);
        }
    }</code></pre>



<p class="wp-block-paragraph">Using the <em>HashMap</em> we can retrieve the <em>CommPortIdentifier</em> object from the string that was mapped earlier.&nbsp; This is achieved through the <em>HashMap’s</em> <em>get()</em> method.&nbsp; The object must also be casted as a <em>CommPortIdentifier</em> because the <em>get()</em> method has a return type of <em>Object</em>.</p>



<p class="wp-block-paragraph">The <em>setConnected </em>method just changes a boolean flag so that the program can store whether or not it is connected to a serial port or not.</p>



<p class="wp-block-paragraph">The <em>CommPort</em> must also be initialized because the port object will be stored here once a successful connection is made.</p>



<p class="wp-block-paragraph">The main method of interest here is the <em>open()</em> method.&nbsp; This instructs the program to open the port, and this method will return the object for the opened port, which I store in in the previously initialized <em>CommPort </em>object.&nbsp; I then cast this object as a <em>SerialPort</em> and store it as well.&nbsp; This is helpful for accessing the methods and variables specific to the <em>SerialPort</em> class.</p>



<p class="wp-block-paragraph">It should also be noted that the <em>open()</em> method requires the use of a try-catch block.&nbsp; So applying that, I catch two different exceptions.&nbsp; The <em>PortInUseException</em> is what the name says.&nbsp; If the port is in use, then this exception is thrown.&nbsp; The next catch block is just for the generic exceptions that occur.&nbsp; I never came across that during testing, nor do I know how to replicate that.</p>



<p class="wp-block-paragraph">I neglected to include any code for setting the baud rate and other settings on the XBee’s (see Hardware Configuration), because I already set those parameters previously using X-CTU (more on setting up XBee’s here).</p>



<h1 class="wp-block-heading">Initializing the Input and Output Streams</h1>



<p class="wp-block-paragraph">This method is pretty short and is pretty straightforward to read.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">    //open the input and output streams
    //pre style="font-size: 11px;": an open port
    //post: initialized input and output streams for use to communicate data
    public boolean initIOStream()
    {
        //return value for whether opening the streams is successful or not
        boolean successful = false;

        try {
            //
            input = serialPort.getInputStream();
            output = serialPort.getOutputStream();
            writeData(0, 0);

            successful = true;
            return successful;
        }
        catch (IOException e) {
            logText = "I/O Streams failed to open. (" + e.toString() + ")";
            window.txtLog.setForeground(Color.red);
            window.txtLog.append(logText + "n");
            return successful;
        }
    }</code></pre>



<p class="wp-block-paragraph">The streams are initialized by returning the input and output streams of the open serial port.&nbsp; In order for the serial port object to not be null, it must store the object for the open serial port.</p>



<p class="wp-block-paragraph">The <em>getInputStream()</em> and <em>getOutputStream()</em> methods both require a try-catch block.&nbsp; The important exception to catch here is the <em>IOException</em> to signify whether the streams failed to open or not.&nbsp; For more information see How to Open A Serial Port in the Reference Material.</p>



<p class="wp-block-paragraph">I also call a method called <em>writeData(0, 0)</em>.&nbsp; This is the method that encapsulates the code required to write serial data.&nbsp; More on that later.&nbsp; Right now it is just used to set variables on the microcontroller side to zero.</p>



<h1 class="wp-block-heading">Setting Up Event Listeners to Read Data</h1>



<p class="wp-block-paragraph">Once the port is open, the serial port must know whenever there is data to be read.&nbsp; This approach is event driven rather than by polling.&nbsp; When the event is hit, a special block of code will run.&nbsp; This is advantageous to polling because polling requires constantly asking if data is available.&nbsp; The code for the event driven approach is below.&nbsp; See Event Based Two Way Communication for more information.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">    //starts the event listener that knows whenever data is available to be read
    //pre style="font-size: 11px;": an open serial port
    //post: an event listener for the serial port that knows when data is received
    public void initListener()
    {
        try
        {
            serialPort.addEventListener(this);
            serialPort.notifyOnDataAvailable(true);
        }
        catch (TooManyListenersException e)
        {
            logText = "Too many listeners. (" + e.toString() + ")";
            window.txtLog.setForeground(Color.red);
            window.txtLog.append(logText + "n");
        }
    }</code></pre>



<p class="wp-block-paragraph">The code here is pretty much the whole reason why this class implements <em>SerialPortEventListener</em>.&nbsp; The parameter for adding the event listener is just the object itself.&nbsp; The base class has a method called <em>serialEvent(event)</em> that should be overridden before the event code will work.&nbsp; Overriding that method defines what happens when the event is hit.</p>



<p class="wp-block-paragraph">The method <em>addEventListener(this)</em> is the event that is always checking for <em>SerialEvents</em>.&nbsp; It isn’t really important to know what the events actually are, just that the one we’re interested in is the one that tells us whenever data is received.&nbsp; Adding the event listener is complemented by the method <em>notifyOnDataAvailable(true)</em>, which definitely helps us achieve what I mentioned previously.</p>



<p class="wp-block-paragraph">This code requires a try-catch box.&nbsp; The exception here is that there may be too many event listeners.&nbsp; I only use one in the code, so this exception should never be hit.</p>



<h1 class="wp-block-heading">Disconnecting from the Serial Port</h1>



<p class="wp-block-paragraph">Once all the communication is completed, the serial port must be disconnected.&nbsp; In some instances, some ports stay stuck open, which isn’t exactly a good thing.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">    //disconnect the serial port
    //pre style="font-size: 11px;": an open serial port
    //post: closed serial port
    public void disconnect()
    {
        //close the serial port
        try
        {
            writeData(0, 0);

            serialPort.removeEventListener();
            serialPort.close();
            input.close();
            output.close();
            setConnected(false);
            window.keybindingController.toggleControls();

            logText = "Disconnected.";
            window.txtLog.setForeground(Color.red);
            window.txtLog.append(logText + "n");
        }
        catch (Exception e)
        {
            logText = "Failed to close " + serialPort.getName()
                              + "(" + e.toString() + ")";
            window.txtLog.setForeground(Color.red);
            window.txtLog.append(logText + "n");
        }
    }</code></pre>



<p class="wp-block-paragraph">The code here requires a try-catch block so there it is.&nbsp; The exception to catch here is pretty generic.</p>



<p class="wp-block-paragraph">Before closing the port, I reset the data on the microcontroller.&nbsp; That step is optional depending on the application.&nbsp; For example, if the value sent to the microcontroller is controlling the speed of a motor, I’d want the motor to turn off if I turn off the remote control.</p>



<p class="wp-block-paragraph">The event listener should be removed as it is no longer needed.</p>



<p class="wp-block-paragraph">The method to close the port is the <em>close()</em> method.</p>



<p class="wp-block-paragraph">The input and output streams should also be closed via the same method as above.</p>



<p class="wp-block-paragraph">See How to Close A Serial Port in the Reference section for more information.</p>



<h1 class="wp-block-heading">Reading Data – The <em>serialEvent</em> Method</h1>



<p class="wp-block-paragraph">This is a follow-up to the section on preparing to Read Data.&nbsp; It has information on how to process the data that the program reads.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">    //what happens when data is received
    //pre style="font-size: 11px;": serial event is triggered
    //post: processing on the data it reads
    public void serialEvent(SerialPortEvent evt) {
        if (evt.getEventType() == SerialPortEvent.DATA_AVAILABLE)
        {
            try
            {
                byte singleData = (byte)input.read();

                if (singleData != NEW_LINE_ASCII)
                {
                    logText = new String(new byte[] {singleData});
                    window.txtLog.append(logText);
                }
                else
                {
                    window.txtLog.append("n");
                }
            }
            catch (Exception e)
            {
                logText = "Failed to read data. (" + e.toString() + ")";
                window.txtLog.setForeground(Color.red);
                window.txtLog.append(logText + "n");
            }
        }
    }</code></pre>



<p class="wp-block-paragraph">Since we only want to read data if it exists, we test the condition in the if statement above.&nbsp; This could be redundant because we wouldn’t be in this method if the event never got triggered.</p>



<p class="wp-block-paragraph">Reading the data requires a try-catch block.&nbsp; The <em>read()</em> method returns an integer, but the value in it is actually a byte value so this is casted to a byte, in which I store in a variable called <em>singleData</em>.&nbsp; When I was testing this code before, I noticed that the data being read would always have random new line characters in it, and I never knew why this happened.&nbsp; I never wrote any new line characters.&nbsp; Since I don’t want these, I add an if condition saying that if it encounters that, don’t display that data on the screen.</p>



<p class="wp-block-paragraph">Java also has a nice way of converting bytes to a string.  A byte array can be used as a parameter when initializing a string, which is what I’ve done when I assign a value to <em>logText</em>.</p>



<p class="wp-block-paragraph">The exception here is just the usual generic one.</p>



<h1 class="wp-block-heading">Writing Data</h1>



<p class="wp-block-paragraph">Here is the method for writing data.&nbsp; In my application, I am writing two integers sequentially to my microcontroller.&nbsp; Data that is sent to the MCU can be used for such things such as controlling motor speeds, which is what I am trying to do here.&nbsp; I hope to use the data sent do control the PWM duty cycle of the motor for speed control.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">    //method that can be called to send data
    //pre style="font-size: 11px;": open serial port
    //post: data sent to the other device
    public void writeData(int leftThrottle, int rightThrottle)
    {
        try
        {
            output.write(leftThrottle);
            output.flush();
            //this is a delimiter for the data
            output.write(DASH_ASCII);
            output.flush();

            output.write(rightThrottle);
            output.flush();
            //will be read as a byte so it is a space key
            output.write(SPACE_ASCII);
            output.flush();
        }
        catch (Exception e)
        {
            logText = "Failed to write data. (" + e.toString() + ")";
            window.txtLog.setForeground(Color.red);
            window.txtLog.append(logText + "n");
        }
    }</code></pre>



<p class="wp-block-paragraph">The <em>write() </em>method here requires a try-catch block just like many of the other methods used for serial communication.&nbsp; Using the output stream, I call that method to write an integer value.&nbsp; I also write a dash and a space to separate the actual data being read.&nbsp; See the ASCII Table for the values of what these characters should be.&nbsp; This helps me differentiate the sets of data that is sent back and forth.&nbsp; The dash separates the two integers being sent and the space separates the entire set.&nbsp; On the Arduino end, four called to <em>read()</em> can separate the data without mixing things up.&nbsp; Example of that later.</p>



<h1 class="wp-block-heading">Putting All the Java Code Together</h1>



<p class="wp-block-paragraph">Once all these methods are setup, they need to be called somewhere in the program so we can put them to use.&nbsp; When the program starts, the code for searching for serial ports will run so that it can populate the combo box on the GUI.&nbsp; The GUI buttons also need to be disabled to reflect the fact they should be disabled when there is no connection to a port.&nbsp; When the connect button is pressed, the input and output streams are started and the event listener is added.&nbsp; The code for that is shown below.</p>



<pre class="wp-block-code"><code lang="java" class="language-java">        private void btnConnectActionPerformed(java.awt.event.ActionEvent evt) {
            communicator.connect();
            if (communicator.getConnected() == true)
            {
                if (communicator.initIOStream() == true)
                {
                    communicator.initListener();
                }
            }
        }</code></pre>



<p class="wp-block-paragraph">The if statement is needed such that the code will not run if a successful connection was not made.&nbsp; This is the precondition for the <em>initIOStream() </em>and the <em>initLIstener() </em>methods.</p>



<p class="wp-block-paragraph">When the user decides to disconnect from the serial port, the user only needs to call the <em>disconnect()</em> method when the disconnect button is pressed.</p>



<p class="wp-block-paragraph">The up/down arrows on my GUI are set to write data to the serial port when they are pressed.&nbsp; When data is written to the Arduino, the MCU will send the data back to the Java program, which will signal the event listener to display that data back to the screen.</p>



<h1 class="wp-block-heading">Arduino Code</h1>



<p class="wp-block-paragraph">The Arduino code is pretty straight forward.&nbsp; There are just a few calls to the <em>read()</em> method.</p>



<p class="wp-block-paragraph">Update 6 May 2012: the syntax for the Arduino code has updated since the writing of this article. &nbsp;Apparently, the lines</p>



<pre class="wp-block-code"><code lang="c" class="language-c">Serial.print(separator, BYTE);
Serial.print(space, BYTE);</code></pre>



<p class="wp-block-paragraph">are no longer valid and have since been replaced by the following:</p>



<pre class="wp-block-code"><code lang="c" class="language-c">Serial.write(byte(separator));
Serial.write(byte(space));</code></pre>



<p class="wp-block-paragraph">The code below hasn&#8217;t been edited with this change, but basically the code required for writing a byte value through serial communication has changed.</p>



<pre class="wp-block-code"><code lang="c" class="language-c">
// this is where we will put our data
int left = 0;
int right = 0;
byte space = 0;
byte separator = 0;

void setup(){
  // Start up our serial port, we configured our XBEE devices for 38400 bps. 
  Serial.begin(9200);
}

void loop(){
  // handle serial data, if any
  if (Serial.available() &amp;gt;= 4){
    left = Serial.read();
    separator = Serial.read();
    right = Serial.read();
    space = Serial.read();
    Serial.flush();

    Serial.print(left);
    Serial.print(separator, BYTE);
    Serial.print(right);
    Serial.print(space, BYTE);
    Serial.print("n");
  }
}</code></pre>



<p class="wp-block-paragraph">I first declare four variables to reflect the fact that I’m writing four different variables to the serial port.</p>



<p class="wp-block-paragraph">In the loop method, I added a condition which will check whether there are four pieces of data available before reading them.&nbsp; This again reflects the fact I’m writing four values.</p>



<p class="wp-block-paragraph">The four variables initially declared store the values in the order that they are written, and then are just printed to the screen.</p>



<p class="wp-block-paragraph">The <em>print()</em> method is able to convert the byte value to an actual character by adding a second parameter called <em>BYTE</em>.&nbsp; The left and right variables don’t need it because the values written were integers to begin with, however, the dash and space characters were written as ASCII values.</p>



<p class="wp-block-paragraph">For more information about Arduino and Java, and Arduino’s Serial library, please refer to those in the Reference Section.</p>



<h1 class="wp-block-heading">Code Downloads</h1>



<p class="wp-block-paragraph">Here are the code downloads for the GUI, key bindings and the serial communication.&nbsp; WordPress doesn’t allow uploads of zip or java files so I uploaded it all to Megaupload.</p>



<p class="wp-block-paragraph">Link to download:&nbsp;<a href="http://www.mediafire.com/?z2l26ncypmzn20z" target="_blank" rel="noopener">http://www.mediafire.com/?z2l26ncypmzn20z</a></p>



<h1 class="wp-block-heading">Other Examples</h1>



<p class="wp-block-paragraph">There are also other sites I looked at which had example code for serial port communication in Java.&nbsp; The sites are Programming Serial and Parallel Ports and Serial Port Access Using RXTX in Java, which are both in the Reference section.</p>



<h1 class="wp-block-heading">Reference Material</h1>



<p class="wp-block-paragraph">Below are a bunch of links to reference sites that I used to find some of the information written here.&nbsp; I mostly referred to bits and pieces of each article to understand the serial code and to come up with the example I present here.&nbsp; The list is in the order that I referenced them.</p>



<h2 class="wp-block-heading">Discovering Available Comm Ports</h2>



<p class="wp-block-paragraph"><a href="http://rxtx.qbang.org/wiki/index.php/Discovering_available_comm_ports" target="_blank" rel="noopener">http://rxtx.qbang.org/wiki/index.php/Discovering_available_comm_ports</a></p>



<p class="wp-block-paragraph">The code on this page is pretty specific to the title of this heading, and the code is pretty easy to follow.</p>



<h2 class="wp-block-heading">How to Open A Serial Port</h2>



<p class="wp-block-paragraph"><a target="_blank" rel="noopener">http://embeddedfreak.wordpress.com/2008/08/08/how-to-open-serial-port-using-rxtx/</a></p>



<p class="wp-block-paragraph">This page provides a very concise and detailed explanation on what each line of code does in order to get a serial port open.&nbsp; Definitely work a look if you’re trying to understand the code.</p>



<h2 class="wp-block-heading">Event Based Two Way Communication</h2>



<p class="wp-block-paragraph"><a href="http://rxtx.qbang.org/wiki/index.php/Event_Based_Two_Way_Communication" target="_blank" rel="noopener">http://rxtx.qbang.org/wiki/index.php/Event_Based_Two_Way_Communication</a></p>



<p class="wp-block-paragraph">This page has example code on how to connect to a serial port and how to interact with it.&nbsp; The code here uses events, rather than polling, to send data back and forth, which means that whenever data is received, the event code is triggered.</p>



<h2 class="wp-block-heading">How to Close A Serial Port</h2>



<p class="wp-block-paragraph"><a target="_blank" rel="noopener">http://embeddedfreak.wordpress.com/2008/08/08/how-to-close-serial-port-in-rxtx/</a></p>



<p class="wp-block-paragraph">This article is written by the same author who wrote the article on how to open serial ports.  Good example code here.</p>



<h2 class="wp-block-heading">ASCII Table</h2>



<p class="wp-block-paragraph"><a href="http://www.asciitable.com/" target="_blank" rel="noopener">http://www.asciitable.com/</a></p>



<p class="wp-block-paragraph">Having the ASCII Table is quite handy for serial data because data being sent and written are bytes, so being able to know what byte values correspond to what character really helps.</p>



<h2 class="wp-block-heading">Arduino and Java</h2>



<p class="wp-block-paragraph"><a href="http://www.arduino.cc/playground/Interfacing/Java" target="_blank" rel="noopener">http://www.arduino.cc/playground/Interfacing/Java</a></p>



<p class="wp-block-paragraph">This article discusses how to use the serial port communication with Java and Arduino, which is exactly what I am after.&nbsp; It has example code on pretty much the entire process of serial port communication, but it may be too much to read since there is little explanation about the code.</p>



<h2 class="wp-block-heading">Arduino Serial Class</h2>



<p class="wp-block-paragraph"><a href="http://arduino.cc/en/Reference/Serial" target="_blank" rel="noopener">http://arduino.cc/en/Reference/Serial</a></p>



<p class="wp-block-paragraph">A good reference for Arduino’s Serial class methods.&nbsp; Also has example code to look at.</p>



<h2 class="wp-block-heading">Programming Serial and Parallel Ports</h2>



<figure class="wp-block-embed is-type-rich is-provider-embed-handler wp-block-embed-embed-handler"><div class="wp-block-embed__wrapper">
https://docs.google.com/viewer?url=http%3A%2F%2Fjava.sun.com%2Fdeveloper%2FBooks%2Fjavaprogramming%2Fcookbook%2F11.pdf
</div></figure>



<p class="wp-block-paragraph">This article provides a brief introduction to programming serial ports and has information on the steps required to talk to the serial port.&nbsp; However, the code examples in there may be hard to follow for someone new to serial programming.</p>



<h2 class="wp-block-heading">Serial Port Access Using RXTX in Java</h2>



<p class="wp-block-paragraph"><a href="http://gomultidomain.blogspot.com/2008/06/serial-port-access-using-rxtx-in-java.html" target="_blank" rel="noopener">http://gomultidomain.blogspot.com/2008/06/serial-port-access-using-rxtx-in-java.html</a></p>



<p class="wp-block-paragraph">This page also has complete code for serial communication in Java, but what I was looking for here was how to use the write method to send serial data from Java.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.henrypoon.com/blog/2011/01/01/serial-communication-in-java-with-example-program/feed/</wfw:commentRss>
			<slash:comments>144</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">566</post-id>	</item>
		<item>
		<title>Axis and Allies 1940 Battle Calculator/Simulator Updated to v1.0</title>
		<link>https://blog.henrypoon.com/blog/2010/12/29/axis-and-allies-1940-battle-calculatorsimulator-updated-to-v1-0/</link>
					<comments>https://blog.henrypoon.com/blog/2010/12/29/axis-and-allies-1940-battle-calculatorsimulator-updated-to-v1-0/#respond</comments>
		
		<dc:creator><![CDATA[hp]]></dc:creator>
		<pubDate>Thu, 30 Dec 2010 07:27:03 +0000</pubDate>
				<category><![CDATA[computer stuff]]></category>
		<category><![CDATA[9]]></category>
		<category><![CDATA[Chan]]></category>
		<category><![CDATA[Computing]]></category>
		<category><![CDATA[D]]></category>
		<category><![CDATA[Download]]></category>
		<category><![CDATA[Geeknet]]></category>
		<category><![CDATA[It]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Lie]]></category>
		<category><![CDATA[Link]]></category>
		<category><![CDATA[Net]]></category>
		<category><![CDATA[Page]]></category>
		<category><![CDATA[Source code]]></category>
		<category><![CDATA[SourceForge]]></category>
		<category><![CDATA[Z]]></category>
		<category><![CDATA[Zip]]></category>
		<guid isPermaLink="false">http://henrypoon.mooo.com/blog/axis-and-allies-1940-battle-calculatorsimulator-updated-to-v1-0</guid>

					<description><![CDATA[No big changes really, other than the fact that there is now a button for swapping attacker and defender so that people can see how the odds changed if the roles were reversed. For more information about the program, see the original post about it. Requires Java to be installed.&#160; Link for that here. SourceForge [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">No big changes really, other than the fact that there is now a button for swapping attacker and defender so that people can see how the odds changed if the roles were reversed.</p>



<p class="wp-block-paragraph">For more information about the program, see the <a href="https://henrypoon.wordpress.com/2010/12/24/axis-and-allies-1940-battle-calculator/" target="_blank" rel="noopener noreferrer">original post</a> about it.</p>



<p class="wp-block-paragraph">Requires Java to be installed.&nbsp; Link for that <a href="https://www.java.com/en/download/manual.jsp" target="_blank" rel="noopener noreferrer">here</a>.</p>



<p class="wp-block-paragraph">SourceForge Page: <a href="http://sourceforge.net/projects/aa40battlecalc/" target="_blank" rel="noreferrer noopener">http://sourceforge.net/projects/aa40battlecalc/</a></p>



<p class="wp-block-paragraph">Download: <a href="http://sourceforge.net/projects/aa40battlecalc/files/Battle%20Calculator%20Binary/v1.0/BattleCalculator-v1.0.jar/download" target="_blank" rel="noreferrer noopener">http://sourceforge.net/projects/aa40battlecalc/files/Battle%20Calculator%20Binary/v1.0/BattleCalculator-v1.0.jar/download</a></p>



<p class="wp-block-paragraph">Source Code: <a href="http://sourceforge.net/projects/aa40battlecalc/files/Battle%20Calculator%20Source/v1.0/src-v1.0.zip/download" target="_blank" rel="noreferrer noopener">http://sourceforge.net/projects/aa40battlecalc/files/Battle%20Calculator%20Source/v1.0/src-v1.0.zip/download</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.henrypoon.com/blog/2010/12/29/axis-and-allies-1940-battle-calculatorsimulator-updated-to-v1-0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">553</post-id>	</item>
	</channel>
</rss>
