tag:blogger.com,1999:blog-61723699769359192024-03-19T14:28:12.804-07:00dale ansonsoftware development, technology, music, whateverDalehttp://www.blogger.com/profile/10665609276390432674noreply@blogger.comBlogger141125tag:blogger.com,1999:blog-6172369976935919.post-52187679156536547642024-02-27T12:11:00.000-08:002024-02-27T12:11:02.611-08:00Youtube Alternative<p>Recently, I was listening to a longish classical music piece on Youtube. Right in the middle of a movement, the video stops to show an ad. How annoying! I don't use Youtube very often, so I don't see the sense in buying Youtube Premium. However, for the few things that I do watch, I don't want the videos interrupted by ads in the middle. First, that should just be against the law, second, I wrote my own front end to Youtube -- no ads!</p><p><br /></p><p>The parts are pretty simple, it's just search, display results, then show the selected video. I wrote it all in Java as that's what I'm most familiar with. The actual playing of videos is passed off to a local video player, both mpv and vlc work well for playing an http stream.</p><p><br /></p><p><a href="https://drive.google.com/file/d/1tUke7HqRp4JxUiLZKCfyEqmqxgkDGrHl/view?usp=sharing" target="_blank">The complete code is here</a>, what follows is the description of what it does.</p><p><br /></p><p>Search on Youtube is just this URL:</p><p><span style="font-family: courier; font-size: x-small;"><br /></span></p><p><span style="font-family: courier; font-size: x-small;">https://www.youtube.com/results?search_query=search+words+here</span></p><p><br /></p><p>The search results are mostly a lot of json, so a few regular expressions work well to extract the relevant content. In the code, the first regex pulls in those sections of the json that cover the videos, the subsequent regular expressions separate out the individual parts of each section. So a 'section' has the video ID, a url for a thumbnail, and a description of the video from the accessibility content. I display these with a button to play the video and a second button to download the video.</p><p><br /></p><p>Getting a video from Youtube is just this URL;</p><p><span style="font-family: courier; font-size: x-small;"><br /></span></p><p><span style="font-family: courier; font-size: x-small;">https://www.youtube.com/watch?v=videoID</span></p><p><br /></p><p>Both <a href="https://mpv.io/" target="_blank">mpv</a> and <a href="https://www.videolan.org/" target="_blank">vlc</a> will play an http stream from a url just by passing the url on the command line, like</p><p><span style="font-family: courier; font-size: x-small;"><br /></span></p><p><span style="font-family: courier; font-size: x-small;">mpv https://www.youtube.com/watch?v=Z8zk7XKyoE8</span></p><p><br /></p><p>For the download, I'm using <a href="https://github.com/yt-dlp/yt-dlp" target="_blank">yt-dlp</a>. I added a lame menu item to set the download directory, it's not a file chooser, which is why it's lame, you have to type in the path yourself. The command line for yt-dlp looks like this:</p><p><span style="font-family: courier; font-size: x-small;">yt-dlp -P downloadDirectory https://www.youtube.com/watch?v=videoId</span></p><p><br /></p><p><span style="font-family: inherit; font-size: x-small;">Here's a screenshot:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTI5Xee8Ps_yFQUQyNANd9NRc6kNBzD_zxuu4wCW-2HngV61Poh7aqUTXNuMf4-KFj3ynZmjVdSKWbXtE5t2OC6DeIABGycXsMPfwv74x2xlnWB5BWlHtsbpLK3n-rUwAfs-bk320rlp07yoKMl_eufHysMoFls_iEUdbnaLTTgeK_yDUEz5xOrgqSj54/s1135/screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="921" data-original-width="1135" height="520" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTI5Xee8Ps_yFQUQyNANd9NRc6kNBzD_zxuu4wCW-2HngV61Poh7aqUTXNuMf4-KFj3ynZmjVdSKWbXtE5t2OC6DeIABGycXsMPfwv74x2xlnWB5BWlHtsbpLK3n-rUwAfs-bk320rlp07yoKMl_eufHysMoFls_iEUdbnaLTTgeK_yDUEz5xOrgqSj54/w640-h520/screenshot.png" width="640" /></a></div><br /><span style="font-family: inherit; font-size: x-small;"><br /></span><p></p><p><span style="font-family: inherit; font-size: x-small;"><br /></span></p><p><span style="font-family: inherit; font-size: x-small;">That's it. Lame, but useful. </span></p>
<p></p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-24230057275897914752023-07-11T07:57:00.003-07:002023-07-11T07:57:35.119-07:00Install Kodi on Raspberry Pi from Linux<p>A short article about how to install Kodi on a Raspberry Pi from Linux. I used to use the LibreELEC USB-SD Creator program, but it no longer works on Linux or Mac. The LibreELEC forum says that's due to too many dependencies on Qt. Oh well, it's not that hard to install without the spiffy GUI. There are probably other ways to get LibreELEC on a Raspberry Pi, but this is what worked for me and is pretty straightforward</p><p><br /></p><p>The Raspberry Pi I have is somewhat older now, maybe 4 years. For some reason, it gave up the ghost and wouldn't boot. I'd just get the LibreELEC start up screen, then after a while, kernel panic and nothing more. So I went to reinstall the OS, that's when I found out the GUI installer no longer works, even the one I had previously installed on my laptop. </p><p><br /></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLQJZG221GH6uxGOVrmx31XQc1-rysSPTxT-hZcBliEr18pQssBHA7ykPJfNKRM74RQE75mnl4n6-S8jfTFV2iacJFCiJiK2jyn2bD1enGNbk5No24GGeOcYSDxLAsF3lyQtTaZaN4lmsZZweBms_FlecSx2cP9Nnk6Zfs2rj5kkceMiomr3U0e8mHCXA/s419/rpi4gb.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="250" data-original-width="419" height="191" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLQJZG221GH6uxGOVrmx31XQc1-rysSPTxT-hZcBliEr18pQssBHA7ykPJfNKRM74RQE75mnl4n6-S8jfTFV2iacJFCiJiK2jyn2bD1enGNbk5No24GGeOcYSDxLAsF3lyQtTaZaN4lmsZZweBms_FlecSx2cP9Nnk6Zfs2rj5kkceMiomr3U0e8mHCXA/s320/rpi4gb.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">2 GB Raspberry Pi</td></tr></tbody></table><br /><p>I put it in this really nice box, just for Kodi:</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcB_MgmevoOaYZS5d7Ep97n5M-wOgzIWJgfRmMtsxOPQGOHYNNehAGGELZPMXaMGCn20wn3nKqvodEHR8J19TQQOfemYQdxQdmdlt8p7DVJY_7uLmMeFCDGxbSBYTkcGpItU0GAA5gl66S5ZWLYWlgWnPmZIRX97zjZaIFm1QGgghX_d897_9QMnizLGU/s466/kodibox.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="156" data-original-width="466" height="107" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcB_MgmevoOaYZS5d7Ep97n5M-wOgzIWJgfRmMtsxOPQGOHYNNehAGGELZPMXaMGCn20wn3nKqvodEHR8J19TQQOfemYQdxQdmdlt8p7DVJY_7uLmMeFCDGxbSBYTkcGpItU0GAA5gl66S5ZWLYWlgWnPmZIRX97zjZaIFm1QGgghX_d897_9QMnizLGU/s320/kodibox.jpg" width="320" /></a></div><br /><p>I'm running Arch Linux, but these instructions should work on any Linux distro, and would work for a USB stick also:</p><p><br /></p><p></p><ol style="text-align: left;"><li>Download the latest image of LibreELEC from <a href="https://libreelec.tv/downloads/raspberry/">https://libreelec.tv/downloads/raspberry/</a><br /><br />Current image is LibreELEC-RPi4.arm-11.0.1.img.gz, but get whatever is the latest.<br /><br /></li><li>Unzip the image: tar -xvzf LibreELEC-RPi4.arm-11.0.1.img.gz<br /><br /></li><li>Insert an empty SD card into the computer. Find it with lsblk, it's probably /dev/sdb, but check to be sure for the next step.<br /><br /></li><li>Burn the image to the SD card with (change the sdX as needed):<br /><br />sudo dd if=LibreELEC-RPi4.arm-11.0.1.img of=/dev/sdX bs=4M<br /><br />Of course, use the actual filename of the image you downloaded and unzipped.<br /><br /></li><li>Plug the SD card into the Kodi box and boot. That should be it.</li></ol><p></p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-12801251360669352972022-11-08T16:56:00.000-08:002022-11-08T16:56:34.908-08:00How to add a URL to the home screen on Android<p>Yeah, so this took me an hour or so to figure out. I live on a beach on the ocean, and I have a boat, so I want to know the tides. The most accurate website I've found for my area is <a href="http://tideking.com">tideking.com</a>. On my laptop, I have a bookmark for the right page on their site, which, of course, goes right to it every time. On my phone, I tried the usual -- go to the site, find the right page, click the hamburger (the 3 dot thing), and click add to home screen. Yep, there is a link on my home screen, but it goes to their main page, not my specific page. I tried this in Chrome, Firefox, and Brave, all the same, it takes me to someplace I've never heard of, not my bookmarked place. Apparently, this is controlled by the website, not me, which is bullshit.</p><p>Here is the best answer to go to any URL directly from your home screen:</p><p><a href="https://play.google.com/store/apps/details?id=com.deltacdev.websiteshortcut">https://play.google.com/store/apps/details?id=com.deltacdev.websiteshortcut</a></p><p>It's an app called Website Shortcut. It's free, it doesn't have any ads, and it works as expected. Go to your page, copy the URL, open Website Shortcut, paste it in, pick an icon, and give it a name. Put it wherever you want on your home screen Done! It goes directly to your desired page, no problems. I rated it 5 stars.</p><p><br /></p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-23771275563263677472022-09-29T18:21:00.001-07:002022-09-29T18:21:40.409-07:00 A short review of the new(ish) HP Dev One laptop<p> I finally put my ageing ThinkPad T420 to rest. It was a great laptop and served me well for 11 (eleven!!!) years. It did almost everything I wanted, but it was getting old and slow, much like me. It seemed it was taking longer and longer to compile code, and it couldn't process 4k video from my GoPro camera. I'd read some great reviews about the HP Dev One laptop, so I ordered one, and I can't say I'm disappointed at all. </p><p><br /></p><p>What I like:</p><p>Just about everything. It came with Pop!_OS, which I tried for about an hour then installed Arch, because that's what I've been running for years. Nothing against Pop, it's just not my thing.</p><p>It's fast, the screen is very readable, the keyboard is okay. I grew up on IBM Model M keyboards, so I usually use an external clicky keyboard. I'm currently using a WASD keyboard with Cherry MX Green, which feels a lot like my old Model M's.</p><p>The touchpad is great, although I do tend to use an external mouse when I can.</p><p><br /></p><p>What I don't like:</p><p>Well, nothing really. The only nit I have is that the function keys are not function keys by default. Instead, the "action" keys, that is, the volume, brightness, etc, keys are the default. This is a laptop aimed at developers, and I use the function keys all the time. I have all of them, F1 through F12 mapped to some action in my code editor. Fortunately, there is a BIOS setting to change it so the function keys are function keys by default. I would have thought someone at HP would have thought of having it the other way around, since I'm sure I'm not alone in using the crap out of function keys.</p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-33056001155845023162022-09-04T14:56:00.003-07:002022-09-04T14:56:36.520-07:00Javadoc<p> Yay! Another post in the same year!</p><p><br /></p><p>I've been writing java code since the beginning of Java. The API documentation has been fantastic, it's all in a set of web pages that can be downloaded locally so I can browse it any time I want. Since Java 10 or so, though, the web pages suck. There used to be frames with a tree on the left, which made it easy to click around and find things. The new pages have a search box and no tree, so you have to know what you're looking for, no more discovery. </p><p><br /></p><p>Well, today I got tired of using Java 8 javadoc since it's way out of date. I downloaded the Java 18 javadoc. It still sucks for navigation. A google search led me to <a href="https://github.com/climber09/Javadoc-Frames-Generator" target="_blank">https://github.com/climber09/Javadoc-Frames-Generator</a>. This works like a charm! It depends on <a href="https://phantomjs.org/" target="_blank">Phantomjs</a>, which is no longer under development, but that doesn't matter because it works well in its current state. So the steps to install:</p><p><br /></p><p></p><ol style="text-align: left;"><li>download and unzip phantomjs somewhere.</li><li>download and unzip javadoc-frames-generator somewhere.</li><li>in a terminal, cd to the javadoc-frames-generator directory</li><li>run /path/to/phantomjs ./jf-generator.js /path/to/javadocs/docs/api</li><li>wait for it to finish</li><li>open /path/to/javadocs/docs/api/main.html in your browser. Bookmark it for later use. Done!</li></ol><div><br /></div><div>A big thanks to James P. Hunter for creating this.</div><p></p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-78912150573347317312022-08-19T16:07:00.001-07:002022-08-19T16:07:54.428-07:00Printer set up<p>Wow, it's been over a year since I've posted, I guess I've been busy with other things.</p><p>So I recently got a new laptop. My old T420 Thinkpad was definitely showing it's age. Even with an SSD, it was just slow. My new one is an <a href="https://hpdevone.com/" target="_blank">HP Dev One</a>, and it is very nice and speedy. It came with Pop!_OS, which I tried for about an hour then installed Arch, because that's what I've been running for years.</p><p>Of course, no printers were installed, and it always seems like it takes me a while to figure out the right URL for the network printers. Then I was reading through the CUPS documentation, and found this gem:</p><p><span style="font-family: courier;">/usr/lib/cups/backend/snmp 192.168.4.46</span></p><p><span style="font-family: inherit;">Change the IP address as needed, but you'll get back a lot of info about the printer, in particular, the specific URL you need to enter into CUPS:</span></p><p><span style="font-family: courier;">INFO: Using default SNMP Community public</span></p><p></p><p><span style="font-family: courier;">network lpd://192.168.4.46:515/PASSTHRU "EPSON XP-630 Series" "EPSON XP-630 Series" "MFG:EPSON;CMD:ESCPL2,BDC,D4,D4PX,ESCPR2,END4;MDL:XP-630 Series;CLS:PRINTER;DES:EPSON XP-630 Series;CID:EpsonRGB;FID:FXN,DPA,WFA,ETN,AFN,DAN,WRA;RID:20;DDS:022500;ELG:0D02;SN:573541593138373069;" ""</span></p><div><span style="font-family: inherit;">And there is the answer, </span>lpd://192.168.4.46:515/PASSTHRU. I pasted that into CUPS "add a printer" setup, and it worked perfectly! Plus it shows the model number of the printer, which is also handy.</div>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-60251624649446411592021-07-23T09:26:00.000-07:002021-07-23T09:26:01.984-07:00Arch Linux How to fix "Unrecognized archive format"<p> I went to update my Arch Linux installation this morning, and got this error:</p><p><br /></p>
<code>
<p>danson@deadlock:$ sudo pacman -Syu</p><p>:: Synchronizing package databases...</p><p> core 614.0 KiB 1438 KiB/s 00:00 [-------------------------------] 100%</p><p> extra 614.0 KiB 1498 KiB/s 00:00 [-------------------------------] 100%</p><p> community 614.0 KiB 1204 KiB/s 00:01 [-------------------------------] 100%</p><p>:: Starting full system upgrade...</p><p>error: could not open file /var/lib/pacman/sync/core.db: Unrecognized archive format</p><p>error: could not open file /var/lib/pacman/sync/extra.db: Unrecognized archive format</p><p>error: could not open file /var/lib/pacman/sync/community.db: Unrecognized archive format</p><p> there is nothing to do</p>
</code>
<div><br /></div><div>After a bit of research, it turns out there is some problem with the mirror list. The list is stored in /etc/pacman.d. The fix is to "simply" generate a new mirror list. A little more research led me to</div><div><br /></div><div> <a href="http://archlinux.org/mirrorlist/">http://archlinux.org/mirrorlist/</a> </div><div><br /></div><div>which is a web page that will generate a new mirror list for you. Uncomment the lines you like, then save it to /etc/pacman.d/mirrorlist, overwriting your old mirrorlist file. </div><div><br /></div><div>Next, delete the .db files from /var/lib/pacman/sync, then run</div><div><br /></div><div><code>pacman -Syyu</code></div><div><br /></div><div>and new .db files will be generated and the system update should work fine.</div>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com2tag:blogger.com,1999:blog-6172369976935919.post-32887533808629912021-06-07T14:32:00.001-07:002021-06-07T14:36:39.971-07:00Moving the tabs in the new Firefox<p> So I upgraded my laptop today, the usual pacman -Syu, and got the new version of Firefox, version 89.0. For some reason, browser makers want to put the tabs ABOVE the address bar, which makes no sense, and lots of people are whining about it on the internet. I did a bunch of googling and <a href="https://support.mozilla.org/en-US/questions/1305615" target="_blank">found this solution from Mozilla</a>. That almost worked for this new version, but not quite, and they left out a major step. </p><p>Here's the procedure: </p><ol style="text-align: left;"><li>In Firefox in the address bar, type about:support. Make a note of the location of your profile directory.</li><li>Go to profile directory from step 1. Create a subdirectory named "chrome" if it doesn't already exist.</li><li>Copy the userChrome.css mention in the solution link above by user cor-el (thanks, cor-el! I've also pasted my version below in case cor-el's version disappears for some reason).</li><li>Paste the css you copied in step 3 into a file named 'userChrome.css" into the "chrome" directory you created in step 2.</li><li>Open the userChrome.css file in your favorite editor (I recommend <a href="http://www.jedit.org" target="_blank">jEdit</a>, of course).</li><li>On line 20, there should be an entry for "--menubar-height", it is 0, which moved the tabs in the way of my bookmark bar. If you don't have the bookmark bar, you're probably good to go, almost -- keep reading. If you do, I changed --menubar-height to 41px. You'll also need to make this change in line 30 if you use full screen mode.</li><li>I noticed that if I open several tabs, the tab bar would not expand across the window as expected. I changed line 45, width: 100wv to width: 1000wv, and now the tab bar expands across the window as expected. <br /></li><li>Save the file.</li><li>In the Firefox address bar, type about:config, search for "toolkit.legacy", there should only be one result, toolkit.legacyUserProfileCustomizations.stylesheets. Change it to "true". This is necessary so Firefox will actually read your userChrome.css file at start up.<br /></li><li>Restart Firefox, you should be good to go.</li></ol><p>I also noticed after doing this that the tab height is a little too short, so on line 14, I changed the --tab-min-height setting to 30 instead of 25. It also seems like the text in the tab isn't centered well vertically, so on line 54, I changed the 1px to 5 px, that looks better, I think.<br /></p><p>You can play around with the other settings as needed to make you happy. I wasn't able to figure out how to make the tabs narrower by default, but they do resize correctly when you have many tabs open. <br /></p><p>Here is the full contents of my userChrome.css file:</p><br />
<pre><code>
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
/* TABS: bottom - Firefox 65 and later - updated for 89+ */
/* https://searchfox.org/mozilla-release/source/browser/themes/shared/tabs.inc.css */
/* https://developer.mozilla.org/en-US/docs/Tools/Browser_Toolbox */
/* ROOT - VARS */
/* you can adjust the CSS variables until it looks correct */
/* you can use the Browser Toolbox to get the toolbar heights */
*|*:root {
--tab-toolbar-navbar-overlap: 0px !important;
--tab-min-height: 30px !important;
--tab-min-width: 60px !important;
--tab-caption: -5px; /* caption buttons on tab bar */
--tab-adjust: 3px; /* adjust tab bar */
--menubar-height: 41px; /*30px|41px=11px; caption buttons on menubar*/
--navbar-height: 31px; /*31px*/
--bookmarkbar-height: 26px;
--tabbar-top: calc(var(--menubar-height) + var(--navbar-height) + var(--bookmarkbar-height) + var(--tab-adjust)); /*89+*/
}
/* in case you enable extra toolbars in full screen mode */
*|*:root[inFullscreen] {
--tab-adjust: 3px;
--menubar-height: 0px; /*30px*/
--navbar-height: 31px; /*31px*/
--bookmarkbar-height: 0px; /*26px*/
--tabbar-top: calc(var(--menubar-height) + var(--navbar-height) + var(--bookmarkbar-height) + var(--tab-adjust)); /*89+*/
}
/* TAB BAR - below nav-bar */
#navigator-toolbox toolbar:not(#nav-bar):not(#toolbar-menubar) {-moz-box-ordinal-group:10}
#TabsToolbar {-moz-box-ordinal-group:1000}
#TabsToolbar {
display: block !important;
position: absolute !important;
/* bottom: 0 !important; /* 68-88: BOTTOM */
top: var(--tabbar-top); /* 89+: TOP */
width: 1000vw !important;
}
#tabbrowser-tabs {
width: 100vw !important;
}
/* navigator-toolbox - PADDING */
*|*:root:not([chromehidden*="toolbar"]) #navigator-toolbox {
padding-bottom: calc(var(--tab-min-height) + 1px) !important; /*ADJUST*/
background-color: var(--toolbar-bgcolor) !important;
}
/* TabsToolbar with menubar and titlebar hidden - rules for Firefox 65-73 */
*|*:root[tabsintitlebar]:not([inFullscreen="true"]):not([sizemode="maximized"])
#toolbar-menubar[autohide="true"] ~ #TabsToolbar{
}
/* TABS: height */
#tabbrowser-tabs,
#tabbrowser-tabs > .tabbrowser-arrowscrollbox,
.tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
min-height: var(--tab-min-height) !important;
max-height: var(--tab-min-height) !important;
}
#TabsToolbar {
height: var(--tab-min-height) !important;
margin-bottom: 1px !important;
box-shadow: ThreeDShadow 0 -1px inset, -moz-dialog 0 1px !important; /*OPTIONAL*/
background-color: var(--toolbar-bgcolor) !important;
color: var(--toolbar-color) !important;
z-index: 1 !important;
}
/* indicators *//*
*|*:root[privatebrowsingmode=temporary] .private-browsing-indicator {
position: absolute !important;
display: block !important;
right: 0px !important;
bottom: 0px !important;
width: 14px !important;
pointer-events: none !important;
}
*/
.private-browsing-indicator {display: none !important;}
.accessibility-indicator {display: none !important;}
/* Indicators - HIDE *//*
*|*:root:not([accessibilitymode]) .accessibility-indicator {display: none !important}
*|*:root:not([privatebrowsingmode=temporary]) .private-browsing-indicator {display: none !important}
*/
/* Drag Space */
.titlebar-spacer[type="pre-tabs"],
.titlebar-spacer[type="post-tabs"] {
width: 20px !important;
}
/* Override vertical shifts when moving a tab */
#navigator-toolbox[movingtab] > #titlebar > #TabsToolbar {
padding-bottom: unset !important;
}
#navigator-toolbox[movingtab] #tabbrowser-tabs {
padding-bottom: unset !important;
margin-bottom: unset !important;
}
#navigator-toolbox[movingtab] > #nav-bar {
margin-top: unset !important;
}
/* Hide window-controls and caption buttons on Tab Bar */
#TabsToolbar #window-controls {display: none !important;}
#TabsToolbar .titlebar-buttonbox-container {display: none !important;}
</code></pre>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-74338711523469587862021-05-26T18:48:00.000-07:002021-05-26T18:48:57.439-07:00How to install and remove KDE on Arch Linux<p> So I was just playing around, I used to run KDE all the time (years ago) and thought I'd give it another try. </p><p><br /></p><p>Installation was easy:</p><p>sudo pacman -S xorg plasma plasma-wayland-session kde-applications</p><p><br /></p><p>It turns out that KDE still isn't for me, it's a little slow and clunky for my 10 year old laptop, so I'll remove it. That's a little harder:</p><p><br /></p><p>sudo pacman -Rns plasma plasma-wayland-session kde-applications</p><p><br /></p><p>Didn't work. Notice I didn't include xorg, I probably didn't need it installed along with KDE since I already had it all installed. The problem was dependencies.</p><p><br /></p><p>So I tried just uninstalling plasma:</p><p><br /></p><p>sudo pacman -Rns plasma plasma-wayland-session</p><p><br /></p><p>Nope:</p><p>error: failed to prepare transaction (could not satisfy dependencies)<br />:: removing libksysguard breaks dependency 'libksysguard' required by kdevelop<br />:: removing plasma-workspace breaks dependency 'plasma-workspace' required by kget<br />:: removing libkscreen breaks dependency 'libkscreen' required by lxqt-config<br />:: removing plasma-workspace breaks dependency 'plasma-workspace' required by telepathy-kde-desktop-applets</p><p></p><p></p><p> </p><p>So I'll just remove kdevelop:</p><p><br />danson@deadlock:~$ sudo pacman -Rns kdevelop<br />checking dependencies...<br />error: failed to prepare transaction (could not satisfy dependencies)<br />:: removing kdevelop breaks dependency 'kdevelop' required by kdevelop-php<br /> </p><p>Okay, I'll just remove kdevelop-php: <br /></p><p>danson@deadlock:~$ sudo pacman -Rns kdevelop-php<br />checking dependencies...<br />error: failed to prepare transaction (could not satisfy dependencies)<br />:: removing kdevelop-php breaks dependency 'kdevelop-php' required by umbrello</p><p></p><p></p><p>So remove umbrello: Yep! And it removed kdevelop and kdevelop-php at the same time!<br />danson@deadlock:~$ sudo pacman -Rns umbrello</p><p><br /></p><p>Try to remove plasma again:<br /></p><p>danson@deadlock:~$ sudo pacman -Rns plasma plasma-wayland-session<br />checking dependencies...<br />error: failed to prepare transaction (could not satisfy dependencies)<br />:: removing plasma-workspace breaks dependency 'plasma-workspace' required by kget<br />:: removing libkscreen breaks dependency 'libkscreen' required by lxqt-config<br />:: removing plasma-workspace breaks dependency 'plasma-workspace' required by telepathy-kde-desktop-applets</p><p></p><p>Okay, so whack kget: Yep!<br />danson@deadlock:~$ sudo pacman -Rns kget<br /></p><p><br /></p><p>Try plasma again:</p><p>danson@deadlock:~$ sudo pacman -Rns plasma plasma-wayland-session<br />checking dependencies...<br />error: failed to prepare transaction (could not satisfy dependencies)<br />:: removing libkscreen breaks dependency 'libkscreen' required by lxqt-config<br />:: removing plasma-workspace breaks dependency 'plasma-workspace' required by telepathy-kde-desktop-applets<br />danson@deadlock:~$ sudo pacman -Rns telepathy-kde-desktop-applets<br /></p><p>That worked, also removed lxqt-config, then reinstalled it, since I'm going to be running lqxt.</p><p>Now plasma goes away! <br /></p><p>danson@deadlock:~$ sudo pacman -Rns plasma plasma-wayland-session</p><p><br /></p><p>Reinstall lxqt-config:</p><p>danson@deadlock:~$ sudo pacman -S lxqt-config</p><p>And finally remove the KDE application packages:</p><p>danson@deadlock:~$ sudo pacman -Rns kde-applications</p><p><br /></p><p>That's it. Not particularly straightforward, but easy enough to get all this stuff off of my laptop.<br /></p><p><br /></p><p><br /></p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-55111406125035216242020-09-29T16:06:00.002-07:002020-09-29T16:06:12.387-07:00Freedb, the CD database is gone, here are alternatives<p>Freedb shutdown earlier this year, and it was the default CDDB for Asunder, which is what I usually use for ripping CDs. </p><p>I found two alternatives:</p><ol style="text-align: left;"><li>gnudb.gnudb.org, port 8880 <br />This is a replacement for freedb, with the stated goal of keeping the database alive, free, and active. </li><li>freedb.freac.org, port 80<br />This is another replacement for freedb, and seems to be somewhat more complete than gnudb.</li></ol><p>In Asunder, go to Preferences - Advanced, and fill in one of these server names and port.<br /></p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-62076969594004703712020-06-13T12:55:00.000-07:002020-06-13T12:55:37.244-07:00Arduino IDE and ctagsThis took me a while to figure out -- suddenly, sketches that have been compiling and uploading stopped compiling. The error message was useless:<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">exit status 1<br />Error compiling for board WeMos D1 R1.</span><br />
<br />
I've been using jEdit as my editor for Arduino, there is a setting in the Arduino IDE to use an external editor, and that's what I've been doing. Since *.ino files are essentially C or C++ files, I wanted to use the ctags sidekick in jEdit to see the various declarations. As *.ino is not a standard file name extension for ctags, I added it in my ~/.ctags file like this:<br />
<br />
<span style="font-size: small;"><span style="font-family: "Courier New", Courier, monospace;">--langmap=C++:+.ino</span></span><br />
<br />
It turns out that didn't work for jEdit, for some reason, the plugin doesn't pick up that setting. In the plugin options for the ctags sidekick, there is a place to enter ctags invocation options, so I entered the above line and now the sidekick properly parses *.ino files. Great!<br />
<br />
Not so great. Now nothing will compile in Arduino IDE. It turns out the IDE also uses ctags, and that line in my ~/.ctags file caused the problem. Since I put the same line in jEdit, I didn't really need the .ctags file any more, so I deleted it and sketches compiled and uploaded again. Weird!Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-74479885536730601262020-06-12T14:15:00.001-07:002020-06-14T17:24:27.002-07:00How to Compile iSpindel with Arduino IDE<br />
My next little electronics project is to build an <a href="https://github.com/universam1/iSpindel" target="_blank">iSpindel</a>. This is an open source project for DIY people to build their own Tilt-like specific gravity monitor for beer brewing, so this combines two of my favorite hobbies, brewing and electronics. The basic idea is as the beer ferments the angle of the iSpindel device sinks to a lower angle, and then does some math to figure out the specific gravity. The idea isn't so much to have exact gravity readings, but to know when fermentation is done by seeing the gravity flat-line.<br />
<br />
iSpindel is set up to compile on PlatformIO, but I don't want to have to install and learn how to use a new IDE, so I'm going to do it in Arduino IDE. I realize the project has firmware files that are already compiled and ready to upload, but I want to make a couple of small changes, so I want to be able to compile it myself. The license says it's okay for me to do that:<br />
<br />
"This project is free to use. It's permitted to modify for personal use.<br />
It's not permitted to distribute the modified project. Modification can be<br />
distributed via the official iSpindel release only. It's not permitted to distribute<br />
in a commercial way without permission."<br />
<br />
So I'll make my little changes, and I won't share them with anyone. Hmm.<br />
<br />
Start at https://github.com/universam1/iSpindel<br />
Download the project as a zip file.<br />
Unzipped to ~/src/iSpindel-master<br />
<br />
Go to ~/src/iSpindel-master/pio/src, rename iSpindel.cpp to iSpindel.ino<br />
<br />
Open iSpindel.ino in Arduino IDE, it will asks to make a folder named iSpindel and move the ino file into it. Do it.<br />
<br />
In the ~/src/iSpindel-master/pio/lib folder, package these as individual zip files:<br />
DoubleResetDetector<br />
Globals<br />
MPUOffset<br />
Sender<br />
WiFiManagerKT<br />
tinyexpr<br />
<br />
Install these into Arduino IDE by going to Sketch, Include Library, Add .zip Library, select the zip files. You have to do this one file at a time, and the IDE doesn't remember where it was, so it's kind of a pain.<br />
<br />
Attempting to compile at this point gives an error about missing PubSubClient.h, downloaded from <br />
https://github.com/knolleary/pubsubclient/releases/tag/v2.8<br />
Download the zip file, not the tar.gz<br />
In Arduino IDE, go to Sketch, Include Library, Add .zip library, select the downloaded zip file.<br />
<br />
Skip this step, see below.<br />
<strike>Attempting to compile at this poing give an error about missing I2Cdev.h.</strike><br />
<strike>Go to https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/I2Cdev</strike><br />
<strike>Download I2Cdev.cpp and I2Cdev.h, make a zip file with these, then do the Include Library thing again for this zip file.</strike><br />
<strike>I downloaded them to ~/src/iSpindel-master/pio/lib/I2Cdev.</strike><br />
<br />
Now missing MPU6050.h:<br />
Go to Sketch, Include Library, Library Manager, search for MPU6050, install the one from Electronic Cats, version 0.0.2.<br />
<br />
Now a fatal error, OneWire.h not found, installed DS18B20_RT library version 0.1.6, from Library Manager, it installs OneWire version 2.3.5 as a dependency. This is for the temperature sensor.<br />
<br />
Also, multiple libraries found for I2Cdev.h:<br />
Multiple libraries were found for "I2Cdev.h"<br />
Used: ~/Arduino/libraries/I2Cdev<br />
Not used: ~/Arduino/libraries/MPU6050<br />
<br />
I'm going to delete the Used one and use the Not used one. I just deleted the folder ~/Arduino/libraries/I2Cdev, recompiled, and that error went away. Just skip installing I2Cdev as I mentioned above and this won't be a problem.<br />
<br />
Next, DallasTemperature.h not found. Go to Sketch, Include Library, Library Manager, search for Dallas Temperature, install version 3.8.0. <br />
<br />
Next, RunningMedian.h not found. Go to Sketch, Include Library, Library Manager, search for RunningMedian, install version 0.2.0. <br />
<br />
Next, ThingSpeak.h not found. Install version 1.5.0 from Library Manager.<br />
<br />
Next, BlynkSimpleEsp8266.h. Install BlynkGSM_Manager version 1.0.9 from Library Manager. It wants to also install Blynk version 0.6.1 and TinyGSM version 0.10.5, install all.<br />
<br />
Yay! It compiled! There is one warning:<br />
<br />
WARNING: library MPU6050 claims to run on avr, samd architecture(s) and may be incompatible with your current board which runs on esp8266 architecture(s).<br />
<br />
<br />
Update, June 14, 2020: It compiles. It uploads. It doesn't work. Well, it does, but it's impossible to get to the configuration. I spent the good part of a day trying to get it to work, but no luck. It hangs on fetching the stored json configuration from the file system, but of course, there is no stored json yet since I can't get to the configuration. Hacked around that, then the DoubleResetDetector doesn't work, it just doesn't detect. I'm going to start from scratch and write my own code, borrowing from the iSpindel code as needed. Reading through that code, I can see there is a lot of checking for this and that that really isn't necessary. I pretty much already know how to set up the wifi, read the temperature, and read the motion/gyro/accelerometer. What I don't have is the experience the iSpindel folks have to figure out the math to calculate specific gravity, so I'll be "borrowing" that code.<br />
<br />
<br />
<br />Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com3tag:blogger.com,1999:blog-6172369976935919.post-24627316605188901932020-06-03T09:31:00.000-07:002020-06-03T09:48:19.855-07:00Wifi Volume ControlMostly I play music on my home stereo system via Kodi and the Android Yatse app. Yatse lets me adjust the volume, but if I'm playing something on Youtube or Pandora or even Facebook, there is no way for me to remotely adjust the volume for my outside speakers. So I built a little volume controller and added it to my <a href="http://danson.grafidog.com/2015/05/arduino-speaker-controller.html" target="_blank">speaker controller.</a> This turned out to be a fun little project.<br />
<br />
First, a couple of pictures:<br />
<br />
<div style="clear: both;">
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv0J-IgaDW1wee4voc73OJJR5awgAPe_pq9n7XRx-oY-vsliIifDKRNdmXt3wgOjkQJplniCCOOXLrpFa9r9pQeGWmvGgJonu0LyqZqNno8ej3FLidHgKCO9mqYuksQ2A2XmpzjCzU-r0/s1600/IMG_20200527_134811.jpg" style="clear: both; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv0J-IgaDW1wee4voc73OJJR5awgAPe_pq9n7XRx-oY-vsliIifDKRNdmXt3wgOjkQJplniCCOOXLrpFa9r9pQeGWmvGgJonu0LyqZqNno8ej3FLidHgKCO9mqYuksQ2A2XmpzjCzU-r0/s640/IMG_20200527_134811.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: left;">This shot shows the inside of my speaker controller. There are a lot of parts, I used an old computer case to hold everything together. My original speaker controller is in the middle with all the wires and the blue relays. The box on the right is another speaker switcher, which I kept as part of the system since it does impedance balancing that my controller doesn't do. At the very top are three individual volume knobs, one for each speaker zone around the house. These are manual knobs, there isn't a way to control them remotely. See the wires coming out of the Arduino and going around to the right under the black box? Those are the connections for the volume controller -- power, ground, data, and clock.</td></tr>
</tbody></table>
</div>
<br />
<div style="clear: both;">
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS9iiDCNpaIXcXaOHINK7TakZqFA-Z96-wQQHXd0HpJE_i69CZQhoSvM3v_rXgldWfP_-DUwd1HOWuVOOg_xOOtTeG4LQ9J6UZsiEXq4_-w8BKGbUQ49Ln7Bvf2Ju2feaDUQrpXhpvMSY/s1600/IMG_20200527_135021.jpg" style="clear: both; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS9iiDCNpaIXcXaOHINK7TakZqFA-Z96-wQQHXd0HpJE_i69CZQhoSvM3v_rXgldWfP_-DUwd1HOWuVOOg_xOOtTeG4LQ9J6UZsiEXq4_-w8BKGbUQ49Ln7Bvf2Ju2feaDUQrpXhpvMSY/s640/IMG_20200527_135021.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: left;">This picture shows the volume controller that I built. It's the little green circuit board on the right. It works as a "master" volume in that it adjusts the volume for all three zones at once. This turns out to be fine, it's really the same thing that Yatse does for Kodi, but now I can adjust the volume for other apps as well.</td></tr>
</tbody></table>
</div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div style="clear: both;">
Here's the layout for the volume controller:<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwRWZpzErL4CUJ-9vDQil4Cix9OgJd9ZLOU5sawdA9VAv7O6HzNQjsbeizoMdO5B2MT7WHmeys1i1B9X_4W-hffOJoW7UXg88An7nm02GBhxUavtSKhgapjoS5k0aAm7ZblDl-yUhyphenhyphentos/s1600/volumn-controller-PT2257_pcb.png" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="666" data-original-width="900" height="472" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwRWZpzErL4CUJ-9vDQil4Cix9OgJd9ZLOU5sawdA9VAv7O6HzNQjsbeizoMdO5B2MT7WHmeys1i1B9X_4W-hffOJoW7UXg88An7nm02GBhxUavtSKhgapjoS5k0aAm7ZblDl-yUhyphenhyphentos/s640/volumn-controller-PT2257_pcb.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">What's missing here???</td></tr>
</tbody></table>
</div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div style="clear: both;">
I followed the schematic very closely, but I forgot that the speaker connections have TWO wires each, they all have a ground in addition to in/out. Also, the above has the ground from the PT2257 going to the wrong place. So this is a better picture:<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7NIJO9FeuLXEW762KTALMrdD5i14WlnQGHRYwTQTpTgA4Y0xMbfm-x1JdG7S_RKrrLVXDMyYEJv9UuqMFw9aU956zDhdWPli4okvCsoe80WThreDgArqsvva3t_pnzhnCiZUYiAUrXs8/s1600/volume-controller-PT2257_pcb-corrected.png" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="666" data-original-width="900" height="472" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7NIJO9FeuLXEW762KTALMrdD5i14WlnQGHRYwTQTpTgA4Y0xMbfm-x1JdG7S_RKrrLVXDMyYEJv9UuqMFw9aU956zDhdWPli4okvCsoe80WThreDgArqsvva3t_pnzhnCiZUYiAUrXs8/s640/volume-controller-PT2257_pcb-corrected.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">This one has connections for the ground wires.</td></tr>
</tbody></table>
</div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<div style="clear: both;">
I added the code to run this to <a href="http://danson.grafidog.com/2015/05/arduino-speaker-controller.html" target="_blank">the code for the speaker controller</a>, so it's all in one place. Here are the parts that I added:<br />
<br />
In the "html head" section, I added a little bit of javascript:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"> server.println("<script>");<br /> server.println("function doit(){");<br /> server.println("document.getElementById(\"vF\").submit();}");<br /> server.println("</script></span><br />
<br />
Then in the main body of the html I added a slider to set the volume:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"> server.print("</div><div class='container'><h3>Master Volume</h3><form id='vF' action='/' role='form'>");<br /> server.print("<input type='range' name='volume' min='0' max='79' value='");<br /> server.print(volume);<br /> server.print("' oninput='doit(value)'>");<br /> server.print("</form>");<br /> server.print("<form id='mF' action='/' role='form'>");<br /> server.print("<input type='submit' name='mute' value='Mute'/></form></span><br />
<br />
To handle the form data, I added these lines to the "handleRequest" method:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"> if (request.indexOf("volume=") > -1) {<br /> int start = request.indexOf("volume=") + 7; // 7 is length of "volume="<br /> int end = request.indexOf(" ", start);<br /> volume = request.substring(start, end).toInt();<br /> } else {<br /> if (request.indexOf("mute") > -1) {<br /> volume = 0;<br /> }<br /> }</span><br />
<br /></div>
<div style="clear: both;">
<br />
There are several libraries around for running the PT2257, but it's pretty simple and doesn't really need a whole library for what I want to do, so I only needed to add these few methods:<br />
<br />
void setVolume(int level) {<br />
byte bbbaaaa = calculateVolume(level);<br />
byte aaaa = bbbaaaa & 0b00001111;<br />
byte bbb = (bbbaaaa >> 4) & 0b00001111;<br />
<br />
Wire.beginTransmission(EVC_ADDR);<br />
Wire.write(STEP_10 | bbb);<br />
Wire.write(STEP_1 | aaaa);<br />
Wire.endTransmission();<br />
}<br />
<br />
byte calculateVolume(int level) {<br />
if (level > 79)<br />
level = 79;<br />
if (level < 0)<br />
level = 0;<br />
level = 79 - level; // invert the volume, so 0 is low and 79 is high<br />
uint8_t b = level / 10; // get the most significant digit (eg. 79 gets 7)<br />
uint8_t a = level % 10; // get the least significant digit (eg. 79 gets 9)<br />
b = b & 0b0000111; // limit the most significant digit to 3 bit (7)<br />
return (b << 4) | a; // return both numbers in one byte (0BBBAAAA)<br />
}<br />
<br />
void unmute() {<br />
byte value = MUTE_OFF;<br />
Wire.beginTransmission(EVC_ADDR);<br />
Wire.write(value);<br />
byte error = Wire.endTransmission();<br />
}<br />
<br /></div>
<br />
<div style="clear: both;">
Now the webpage looks like this on my phone:<br />
<br />
<span id="goog_455715826"></span><span id="goog_455715827"></span><span id="goog_1066564308"></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq9H84lPPc-ICxyDnTyxZC_KRyr1t-MjsIquWlCqzfa8iC3C9ypuc4_oMYY2ebZxp3sdSMsQohwE91AicRTA6Mp6pZWAy3xGI1YQI4BBYVc3tnDAu5_btt-FL5odZjbQiaA2ZA1A0Lyew/" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="2220" data-original-width="1080" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq9H84lPPc-ICxyDnTyxZC_KRyr1t-MjsIquWlCqzfa8iC3C9ypuc4_oMYY2ebZxp3sdSMsQohwE91AicRTA6Mp6pZWAy3xGI1YQI4BBYVc3tnDAu5_btt-FL5odZjbQiaA2ZA1A0Lyew/w312-h640/speaker-controller-with-volume.png" width="312" /></a></div>
</div>
<div style="clear: both;">
Here is the complete code. I had to remove a few thing and optimize a bit as this barely fits in the memory of the Arduino.
<br />
<pre><span style="color: #dcdccc;">
<span style="color: #7fb47f;"><i>/*</i></span>
<span style="color: #7fb47f;"><i>Simple</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>web</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>server</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>serve</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>a</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>form</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>turn</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>on</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>or</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>off</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>digital</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pins.</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>In</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>this</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>case,</i></span>
<span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pins</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>are</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>connected</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>relays</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>turn</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speakers</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>on</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>or</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>off.</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>This</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>uses</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pins</i></span>
<span style="color: #7fb47f;"><i>2</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>through</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>9</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>in</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pairs,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>so</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>2</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>and</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>3</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>control</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>hot</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>tub</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speakers,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>4</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>and</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>5</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>control</i></span>
<span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>dining</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>room</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speakers,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>and</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>6</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>and</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>7</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>control</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>portico</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speakers.</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>8</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>and</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>9</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>are</i></span>
<span style="color: #7fb47f;"><i>available</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>in</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>case</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>I</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>ever</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>find</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>a</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>need</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>connect</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>one</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>more</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pair</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>of</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speakers.</i></span>
<span style="color: #7fb47f;"><i>*/</i></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">include</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;"><</span><span style="color: #cc9393;">Ethernet.h</span><span style="color: #cc9393;">></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">include</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;"><</span><span style="color: #cc9393;">SPI.h</span><span style="color: #cc9393;">></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">include</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;"><</span><span style="color: #cc9393;">Wire.h</span><span style="color: #cc9393;">></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">include</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;"><</span><span style="color: #cc9393;">string.h</span><span style="color: #cc9393;">></span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>MAC</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>address</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>can</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>be</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>anything</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>that</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>unique</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>within</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>local</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>network.</i></span>
<span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">mac</span><span style="color: #f0efd0;">[</span><span style="color: #f0efd0;">]</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span><span style="color: #8cd0d3;">0x00</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0x1E</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0x2A</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0x76</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0x24</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0x08</span><span style="color: #f0efd0;">}</span><span style="color: #f0efd0;">;</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>Some</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>unused</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>IP</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>address</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>on</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>local</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>network.</i></span>
<span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">ip</span><span style="color: #f0efd0;">[</span><span style="color: #f0efd0;">]</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span><span style="color: #8cd0d3;">192</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">168</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">2</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">251</span><span style="color: #f0efd0;">}</span><span style="color: #f0efd0;">;</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>web</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>server,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>nothing</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>fancy,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>just</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>port</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>80</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>for</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>http</i></span>
<span style="color: #dcdccc;">EthernetServer</span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>server</b></span><span style="color: #f0efd0;">(</span><span style="color: #8cd0d3;">80</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">define</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0</span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">define</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">1</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>settings</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>for</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>volume</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>controller</i></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">define</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">EVC_ADDR</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0x44</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>volume</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>control</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>chip</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>address</i></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">define</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">STEP_1</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0b11010000</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>2-Channel,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>-1dB/step</i></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">define</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">STEP_10</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0b11100000</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>2-Channel,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>-10dB/step</i></span>
<span style="color: #dddd66;"><b>#</b></span><span style="color: silver;">define</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">MUTE_OFF</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0b01111000</span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>2-Channel</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>MUTE</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>off</i></span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>true,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>just</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>show</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speaker</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>status</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>or</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>false,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>actually</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>change</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speaker</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>states</i></span>
<span style="color: #dcdccc;">boolean</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">showStatus</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>which</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>zones</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>are</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>on</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>or</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>off,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>initially,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>all</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>are</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>off</i></span>
<span style="color: #dcdccc;">boolean</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone1</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;">boolean</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone2</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;">boolean</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone3</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;">boolean</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone4</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>default</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>volume</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>25</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>start</i></span>
<span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">volume</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0</span><span style="color: #f0efd0;">;</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pin</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>definition,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>one</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pin</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>per</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speaker,</i></span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>so</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>left</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speaker</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>in</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>zone</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>1</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pin</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>2,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>etc</i></span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone1L</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">2</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone1R</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">3</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone2L</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">4</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone2R</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">5</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone3L</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">6</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone3R</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">7</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone4L</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">8</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone4R</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">9</span><span style="color: #f0efd0;">;</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>set</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>up</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pins,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>initially</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>all</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speakers</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>in</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>all</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>zones</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>are</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>off</i></span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>setupPins</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone1L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone1R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone2L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone2R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone3L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone3R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone4L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone4R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone1L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone1R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone2L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone2R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone3L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone3R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone4L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>pinMode</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone4R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">OUTPUT</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>delay</b></span><span style="color: #f0efd0;">(</span><span style="color: #8cd0d3;">2000</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>setup</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Ethernet</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>begin</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">mac</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">ip</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>begin</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>begin</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>join</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>i2c</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>bus</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>setupPins</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>Serial.begin(115200);</i></span>
<span style="color: #f0efd0;">}</span>
<span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>set</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>up</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>buffer</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>for</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>reading</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>web</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>requests</i></span>
<span style="color: #f0dfaf;"><b>static</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>const</b></span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bufferMax</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">128</span><span style="color: #f0efd0;">;</span>
<span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bufferSize</span><span style="color: #f0efd0;">;</span>
<span style="color: #cccc66;"><b>char</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">buffer</span><span style="color: #f0efd0;">[</span><span style="color: #dcdccc;">bufferMax</span><span style="color: #f0efd0;">]</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>loop</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">EthernetClient</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">client</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>available</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">client</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>waitForRequest</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">client</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>handleRequest</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">client</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>stop</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>waitForRequest</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">EthernetClient</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">client</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bufferSize</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>while</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">client</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>connected</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">client</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>available</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>char</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">c</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">client</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>read</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">c</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;">'</span><span style="color: #cc9393;">\n</span><span style="color: #cc9393;">'</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>break</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>else</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">bufferSize</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;"><</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bufferMax</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">buffer</span><span style="color: #f0efd0;">[</span><span style="color: #dcdccc;">bufferSize</span><span style="color: #f0efd0;">+</span><span style="color: #f0efd0;">+</span><span style="color: #f0efd0;">]</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">c</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>else</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>break</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>handleRequest</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>Received</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>buffer</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>contains</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>a</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>standard</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>HTTP</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>GET</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>line,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>something</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>like</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>"GET</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>/?X=X&Y=Y</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>HTTP/1.1".</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>Could</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>have</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>up</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>4</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>parameters,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>1=on&2=on&3=on&4=on,</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>one</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>for</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>each</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>set</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>of</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>speakers.</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>All</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>that</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>necessary</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>here</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>extract</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>query</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>string</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>and</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>check</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>for</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>each</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>of</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>zone</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>numbers.</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">String</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">request</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>String</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">buffer</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>int</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>firstSpace</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>request.indexOf("</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>");</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>right</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>after</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>GET</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>int</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>lastSpace</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>request.indexOf("</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>",</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>firstSpace</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>1);</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>just</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>after</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>query</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>string</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>request</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>request.substring(firstSpace,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>lastSpace);</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>Serial.println(request);</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">showStatus</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">?</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">1=on</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone1</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>true</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>else</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">1=off</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone1</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">2=on</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone2</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>true</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>else</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">2=off</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone2</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">3=on</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone3</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>true</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>else</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">3=off</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone3</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">4=on</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone4</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>true</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>else</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">4=off</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone4</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dca3a3;"><b>false</b></span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">volume=</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">start</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">volume=</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">+</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">7</span><span style="color: #f0efd0;">;</span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>7</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>length</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>of</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>"volume="</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">end</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">start</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">volume</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>substring</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">start</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">end</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>toInt</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>else</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">request</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>indexOf</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">mute</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #8cd0d3;">1</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">volume</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>Serial.println("Volume</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>"</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>String(volume));</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">!</span><span style="color: #dcdccc;">showStatus</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>setSpeakerState</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>sendPage</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>setSpeakerState</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone1L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone1</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone1R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone1</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone2L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone2</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone2R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone2</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone3L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone3</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone3R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone3</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone4L</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone4</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalWrite</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone4R</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone4</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_OFF</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>unmute</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>setVolume</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">volume</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>sendPage</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>http</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>response</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>header</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>/*</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>int</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>length</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>1442;</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>if</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(digitalRead(zone4L)</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>==</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>RELAY_ON)</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>length</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>18;</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>if</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(digitalRead(zone3L)</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>==</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>RELAY_ON)</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>length</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>18;</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>if</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(digitalRead(zone2L)</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>==</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>RELAY_ON)</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>length</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>18;</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>if</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(digitalRead(zone1L)</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>==</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>RELAY_ON)</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>length</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>18;</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>*/</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">HTTP/1.1</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">200</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">OK</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">Content-Type:</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">text/html</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>server.print("Content-Length:</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>");</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>server.println(length);</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>html</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>head</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><html><head><meta</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">charset=\"UTF-8\"></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><meta</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">name=\"viewport\"</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">content=\"width=device-width,</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">initial-scale=1\"></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><link</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">rel=\"stylesheet\"</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">href=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css\"></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><title>Speaker</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">Control</title></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><script></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">function</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">doit(){</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">document.getElementById(\"vF\").submit();}</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"></script></head><body></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>html</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>body</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>println</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><div</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">class='container'><h2>Speaker</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">Control</h2><form</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">action='/'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">role='form'></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">pinState</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalRead</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone4L</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>printCheckBox</b></span><span style="color: #f0efd0;">(</span><span style="color: #8cd0d3;">4</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">pinState</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">Hot</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">tub</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">pinState</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalRead</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone3L</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>printCheckBox</b></span><span style="color: #f0efd0;">(</span><span style="color: #8cd0d3;">3</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">pinState</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">Dining</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">Room</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">pinState</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>digitalRead</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone2L</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>printCheckBox</b></span><span style="color: #f0efd0;">(</span><span style="color: #8cd0d3;">2</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">pinState</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">Portico</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>/*</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>not</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>used</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pinState</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>digitalRead(zone1L);</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>printCheckBox(1,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>pinState,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>"(Empty)");</i></span>
<span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>*/</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><br/><button</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">type='submit'>Save</button></form></div></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"></div><div</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">class='container'><h3>Master</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">Volume</h3><form</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">id='vF'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">action='/'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">role='form'></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><input</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">type='range'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">name='volume'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">min='0'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">max='79'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">value='</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">volume</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">oninput='doit(value)'></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"></form></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><form</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">id='mF'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">action='/'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">role='form'></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><input</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">type='submit'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">name='mute'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">value='Mute'/></form></div></body></html></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>printCheckBox</b></span><span style="color: #f0efd0;">(</span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">zone</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">pinState</span><span style="color: #f0efd0;">,</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">String</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">label</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"><br/><input</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">type='hidden'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">name='</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">value='off'/><div</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">class='checkbox'><label><input</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">type='checkbox'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">name='</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">zone</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">'</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">value='on'</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">pinState</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">RELAY_ON</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">?</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"> </span><span style="color: #cc9393;">checked='checked'</span><span style="color: #cc9393;">"</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">:</span><span style="color: #dcdccc;"> </span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;">></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">label</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">server</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>print</b></span><span style="color: #f0efd0;">(</span><span style="color: #cc9393;">"</span><span style="color: #cc9393;"></label></div></span><span style="color: #cc9393;">"</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>setVolume</b></span><span style="color: #f0efd0;">(</span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bbbaaaa</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>calculateVolume</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">level</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">aaaa</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bbbaaaa</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">&</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0b00001111</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bbb</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">bbbaaaa</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">4</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">&</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0b00001111</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>beginTransmission</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">EVC_ADDR</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>write</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">STEP_10</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">|</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">bbb</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>write</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">STEP_1</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">|</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">aaaa</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>endTransmission</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>Serial.println("set</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>volume</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>"</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(79</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>-</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>level));</i></span>
<span style="color: #f0efd0;">}</span>
<span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>calculateVolume</b></span><span style="color: #f0efd0;">(</span><span style="color: #cccc66;"><b>int</b></span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">level</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">></span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">79</span><span style="color: #f0efd0;">)</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">79</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>if</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">level</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;"><</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0</span><span style="color: #f0efd0;">)</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">79</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">-</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #f0efd0;">;</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>invert</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>volume,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>so</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>0</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>low</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>and</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>79</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>is</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>high</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">uint8_t</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">b</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">/</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">10</span><span style="color: #f0efd0;">;</span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>get</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>most</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>significant</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>digit</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(eg.</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>79</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>gets</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>7)</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">uint8_t</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">a</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">level</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">%</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">10</span><span style="color: #f0efd0;">;</span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>get</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>least</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>significant</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>digit</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(eg.</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>79</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>gets</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>9)</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">b</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">b</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">&</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">0b0000111</span><span style="color: #f0efd0;">;</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>limit</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>the</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>most</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>significant</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>digit</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>to</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>3</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>bit</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(7)</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #f0dfaf;"><b>return</b></span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">b</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;"><</span><span style="color: #f0efd0;"><</span><span style="color: #dcdccc;"> </span><span style="color: #8cd0d3;">4</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">|</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">a</span><span style="color: #f0efd0;">;</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>return</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>both</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>numbers</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>in</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>one</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>byte</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(0BBBAAAA)</i></span>
<span style="color: #f0efd0;">}</span>
<span style="color: #f0dfaf;"><b>void</b></span><span style="color: #dcdccc;"> </span><span style="color: #efef8f;"><b>unmute</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">{</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">value</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">MUTE_OFF</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>beginTransmission</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">EVC_ADDR</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>write</b></span><span style="color: #f0efd0;">(</span><span style="color: #dcdccc;">value</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">byte</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">error</span><span style="color: #dcdccc;"> </span><span style="color: #f0efd0;">=</span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;">Wire</span><span style="color: #f0efd0;">.</span><span style="color: #efef8f;"><b>endTransmission</b></span><span style="color: #f0efd0;">(</span><span style="color: #f0efd0;">)</span><span style="color: #f0efd0;">;</span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i>if</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>(error)</i></span>
<span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #dcdccc;"> </span><span style="color: #7fb47f;"><i>//</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>Serial.println("mute,</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>error</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>code</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>=</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>"</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>+</i></span><span style="color: #7fb47f;"><i> </i></span><span style="color: #7fb47f;"><i>String(error));</i></span>
<span style="color: #f0efd0;">}</span>
</span></pre>
</div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-29793748353331111692020-05-23T13:32:00.000-07:002020-05-23T13:32:42.320-07:00How to skip packages when updating ArchI tried to update my Arch Linux installation today, but got an error message when it tried to install Java 14. <br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">error: failed to prepare transaction (could not satisfy dependencies)<br />:: installing jre-openjdk (14.0.1.u7-1) breaks dependency 'java-runtime-openjdk=13' required by java-openjfx</span><br />
<br />
While I do a fair amount of Java development, I'm not ready to switch to Java 14 yet, so I don't really need it to be installed. The way to avoid installing Java 14 with a system update is to do this:<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">sudo pacman -Syu --ignore=jre-openjdk,jre14-openjdk,jdk-openjdk,openjdk-doc,jre-openjdk-headless</span><br />
<br />
It took me a couple of tries to get all the packages listed correctly, but this does it.Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-89920425619683263632019-04-16T17:41:00.005-07:002020-09-22T12:52:09.790-07:00Setting up Kodi to share media library on LibreelecI was looking for a way to keep all of my various Kodi devices in sync, so that I can tell from my bedroom which shows I have seen even if I watched them in the living room. Kodi has a few ways to to this, one using UPnP, another using MySQL. Both have difficulties, in my case, getting UPnP to work across both my ethernet network and my wifi network is a pain to set up and doesn't always work. The MySQL approach, as show on the Kodi wiki website is difficult to set up. There are several guides on the internet about how to do it with Docker containers, which again is a little difficult to do and is way overkill if your main Kodi instance is Libreelec. Libreelec has a standard addon for MySQL (actually MariaDB, which is the open source equivalent of MySQL).<br />
<br />
As far as I know, this only works on Libreelec. I didn't include all the "click here" paths for each step, I'm assuming Kodi is easy enough to use to not need them.<br />
<br />
<b>Prerequisites:</b><br />
<br />
1. Ensure all Kodi instances are running the same major version of Kodi, as in all are running Krypton or all are running Leia, or whatever the latest and greatest is named.<br />
<br />
2. Ensure your media files are accessible from all Kodi instances, that is, you've set up a source for each type of media, music, movies, tv, etc. All of my media files are on a Western Digital "My Cloud" device, which is basically a NAS with a nice web interface. It is accessible via SMB or NFS.<br />
<br />
3. Choose which Kodi instance is the "master". This one needs to be running Libreelec. This is the instance that will be maintaining the video and music sources for the library and hosting the shared library database. In my case, I have a mini-PC with 8 GB RAM and 300 GB of hard drive space connected to my main home theater system, so that's the obvious choice. I have 5 Firesticks, a laptop, and 2 phones with Kodi installed. The laptop would also be a decent choice, except it sometimes leaves the house.<br />
<br />
<b>On the "master":</b><br />
<br />
4. Install the MariaDB/MySQL addon from the Libreelec repository, Add-on - Install from repository - LibreELEC Add-ons - Services - MariaDB SQL database server.<br />
<br />
5. Configure the MariaDB addon, change the root and user passwords to "kodi". There is no need to do any other db stuff, the addon takes care of setting up the db users and media database. I'm assuming this is all running on a private network, so the weak passwords are okay.<br />
<br />
6. Export your current library if you want. I chose not to as it seems like an unnecessary step to me.<br />
<br />
7. Create or update ~/.kodi/userdata/advancedsettings.xml with the following xml. I just created it on my laptop, then copied it to my NAS so it's easy to copy to Kodi via the file manager. In the Kodi file manager, ~/.kodi/userdata is the Profile directory. <br />
<br />
<advancedsettings><br />
<videodatabase><br />
<type>mysql</type><br />
<host>***.***.***.***</host><br />
<port>3306</port><br />
<user>kodi</user><br />
<pass>kodi</pass><br />
</videodatabase><br />
<musicdatabase><br />
<type>mysql</type><br />
<host>***.***.***.***</host><br />
<port>3306</port><br />
<user>kodi</user><br />
<pass>kodi</pass><br />
</musicdatabase><br />
<videolibrary><br />
<importwatchedstate>true</importwatchedstate><br />
<importresumepoint>true</importresumepoint><br />
</videolibrary><br />
</advancedsettings><br />
<br />
Change both of the ***.***.***.*** to the IP address of the master Kodi instance. This tells Kodi to use MariaDB instead of the built-in SQLlite database. Notice the password are in plain text here, if you changed them in the addon settings, change them here too.<br />
<br />
8. Restart the master.<br />
<br />
9. Import your exported library if necessary.<br />
<br />
10. Copy the advancedsettings.xml file to a location accessible to the other Kodi instances.<br />
<br />
<b>On each of the remaining Kodi instances:</b><br />
<br />
11. Open the file manager.<br />
<br />
12. In the left pane, navigate to the "profile" folder. You should see a file named "guisettings.xml" if you are in the right place.<br />
<br />
13. In the right pane, create a new source with the location where you put the copy of advancedsettings.xml<br />
<br />
14. In the right pane, navigate to the advancedsettings.xml file, use the context menu (right click or Ctrl-C) to copy to the left pane.<br />
<br />
15. Restart the Kodi instance.<br />
<br />
Should be done, all media info should automatically be available in each instance. Now shows you watch on one instance will be marked as watched on all other instances, you can pause a movie in one room and pick it up where you left off in another room. Nice!Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0Unnamed Road, Idaho, USA43.0104967 -113.727221112.423171772356934 -148.8834711 73.597821627643057 -78.5709711tag:blogger.com,1999:blog-6172369976935919.post-86715566970043200742019-04-06T12:30:00.004-07:002020-08-20T19:28:36.701-07:00How to install Pandoki in Kodi<p>See <a href="http://danson.grafidog.com/2019/04/how-to-install-kodi-on-firestick_6.html" target="_blank">previous post about Firestick</a>, but this should work on any Kodi installation.<br />
<br />
1. Go to the main Pandoki page: https://forum.kodi.tv/showthread.php?tid=205547<br />
2. Download the repository zip from that page, repository.gominoa-0.1.0.zip. Put it somewhere that you can find it later from Kodi. I have a network storage server that is accessible, so I put it there, this way I can easily install it on multiple Firesticks/Kodi installations.<br />
3. In Kodi, go to Add-Ons, install from zip, install the gominoa repository zip file.<br />
4. In Kodi, go to Add-Ons, install from repository, find the gominoa repository/Music Add-Ons/Pandoki, install.<br />
5. If you get a message about script.module.mutagen not installed, download it from https://github.com/kodil/kodil/blob/master/repo/script.module.mutagen/script.module.mutagen-1.22.0.zip, then Add-Ons, install from zip, then repeat step 4.<br />
<br />
yeah, Dale, look in the big hard drive, all that stuff is there, no need to download it again, and it looks like someone pulled the plug on the mutagen thing, but the one in the big hard drive works fine. On the upside, this post was the 2nd result on google for how to install pandoki!</p><p><br />
<br /></p>Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-61983017165302951032019-04-06T12:23:00.005-07:002019-04-16T15:21:15.010-07:00How to install Kodi on FirestickI've done this several times, so I'm writing it down for easier reference. We have 6 Firesticks now, one for every TV in the house. Installing Kodi on the Firestick means all of the TVs can access our network storage drive and play whatever music, movies, and TV shows we might have on hand.<br />
<br />
1. In the Firestick, go to Settings - My Firestick - Developer options - turn on "Apps from unknown sources"<br />
2. Install the downloader app from Amazon, you can find it in the My Apps section<br />
3. Open the downloader app, go to http://www.kodi.tv/download<br />
4. Select Android<br />
5. Install the 32 bit version, the 64 bit version doesn't install on Firestick for some reason<br />
6. That's it, go configure Kodi<br />
<div>
<br /></div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-66838893204501197802018-09-25T10:41:00.000-07:002019-08-06T13:12:52.815-07:00Ugh. Google Wifi and how to work around itGoogle Wifi is pretty, slick, easy, and lacking necessary features to be real. My wife bought a box of these a couple of weeks ago at Costco (good price!). Super simple to set up, except that Google insists on doing odd things that would make any network admin crazy, and leaves out important features of any network router. Plus there are bugs...<br />
<br />
First, the main bug is port forwarding doesn't work. I'm trying to get my Google Home devices (also from Costco, and also a good price!) to connect to my stereo system. It took a couple of days, but now it's working flawlessly. I had to remove the Google Wifi as the main router since port forwarding simply doesn't work. I wanted 4 ports forwarded:<br />
<br />
<ol>
<li>one for my speaker controller, which turns on or off various speakers around the house.</li>
<li>another for my ddns client, running on an ethernet connected box.</li>
<li>one for a simple python server to intercept IFTTT requests to my amplifier to adjust volume, switch inputs, etc.</li>
<li>and one for a nodejs server also intercepting IFTTT requests to pass to my Kodi box.</li>
</ol>
<div>
The port forwarding rules on the Google Wifi puck work, then they don't, then they work, then they don't. Totally unreliable. I've read lots and lots of complaints about this.</div>
<div>
<br /></div>
<div>
So I removed the Google Wifi as the main router after my DSL modem (Century Link C1000A, I've mentioned it elsewhere) and replaced it with a TP-Link Archer C7 running DD-WRT. Port forwarding is straightforward, always works, no problems.</div>
<div>
<br /></div>
<div>
Next, the crazy shit. </div>
<div>
<br /></div>
<div>
Google Wifi insists on being on a separate network. It insists on being the gateway for all wifi devices that connect, there is no way to set this stuff on the same network as the rest of the ethernet network in my house. What this means is none of my wifi connected devices can see any of the ethernet connected devices unless I plug them all into the Google Wifi devices. I can't do that, because then the port forwarding fails.</div>
<div>
<br /></div>
<div>
It's pretty straightforward to setup a static route on my C7 so traffic my ethernet network (192.168.2.0) can see the wifi devices (192.168.86.x). It is impossible to set a route the other way, so I can't control my Kodi box from my phone anymore, nor my speaker controller, nor my amplifier. My Firesticks can't find my network attached storage, so they can't play my music or movies. This is basic networking stuff, but Google chose to skip it to make it easy for... well, I don't know who. If your entire network is on wifi, then you're good. If you want to print from your wifi connected laptop to your ethernet connected printer, you're screwed.</div>
<div>
<br /></div>
<div>
Here's Googles best answer: <a href="https://support.google.com/wifi/answer/7215624#3rd-party-router">https://support.google.com/wifi/answer/7215624#3rd-party-router</a>. The problem here is I'd have to run separate wires to each Google Wifi puck, it can't just use my existing ethernet, because, well, fuck it, it's just that way.</div>
<div>
<br /></div>
Okay, enough whining about Google Wifi. Fortunately, I still have an old Linksys WRT-45G with DD-WRT installed. It's an actual, full-featured router, so it can actually do routing, unlike the Google pucks. I put this in between the Google wifi network and my ethernet network, set up routing on the Archer C7 and the Linksys so they can route between the two networks, and now all is good -- port forwarding works correctly, my wifi network can see my ethernet network, and everything can get to the internet with no problems.<br />
<br />
Setting this up was pretty straightforward, but not all of it is obvious. I followed instructions that I found here:<br />
<br />
<a href="http://www.patrikdufresne.com/en/multiple-subnets-routing-with-dd-wrt/">http://www.patrikdufresne.com/en/multiple-subnets-routing-with-dd-wrt/</a><br />
<br />
(Many thanks to Patrik Dufresne for these instructions!) Since links sometimes disappear from the internet, I'm putting the details here.<br />
<br />
Here's a picture of what I'm going for:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">internet <-----> Archer C7 <----------> WRT-54G <---------> Google Wifi<br /> WAN: PPPoE WAN: 192.168.2.2 WAN: 192.168.4.2<br /> LAN: 192.168.2.1 LAN: 192.168.4.1 LAN: 192.168.86.1</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"> <span style="font-family: "courier new" , "courier" , monospace;">Rest of ethernet network </span></span></span><br />
<br />
Archer C7 is main gateway router. It's in gateway mode (Setup - Advanced Routing - Operating Mode = Gateway). The LAN network is 192.168.2.0, LAN IP is 192.168.2.1.<br />
<br />
Linksys WRT-45G is in router mode (Setup - Advanced Routing - Operating Mode = Router). The WAN IP is 192.168.2.2, the LAN network is 192.168.4.0, so I set the LAN IP to 192.168.4.1<br />
<br />
The Google Wifi pucks are on the 192.168.86.0 network, the WAN IP is 192.168.4.2.<br />
<br />
<b>Configure the Archer C7:</b><br />
It's already properly configured for both the WAN and LAN sides, but it needs a route to the Linksys. Go to Setup - Advanced Routing and add a static route with this info:<br />
<div>
<br /></div>
<div>
Destination LAN NET: 192.168.4.0<br />
Subnet Mask: 255.255.255.0<br />
Gateway: 192.168.2.2<br />
Interface: ANY<br />
I don't know what "Metric" does, I left it at 0.<br />
<br />
Add a firewall rule so the 192.168.4.0 subnet can access the internet. Go to Administration - Commands, paste in this command<code><span style="font-size: small;">:</span></code><br />
<br />
<span style="font-size: small;"><span style="font-family: inherit;"><code class="language-sh" data-lang="sh">iptables <span class="nt">-t</span> nat <span class="nt">-I</span> POSTROUTING <span class="nt">-o</span> <span class="sb">`</span>get_wanface<span class="sb">`</span> <span class="nt">-j</span> SNAT <span class="nt">--to</span> <span class="sb">`</span>nvram get wan_ipaddr<span class="sb">`</span></code></span></span><br />
<br />
Click "Run Commands", wait for it to finish, then click "Save Firewall" so this setting persists after reboot.<br />
<br />
I turned off the wifi also, there is a button on the back of the Archer C7, plus I disabled the wifi in Wireless - Basic Settings, Wireless Network Mode = Disabled. <br />
<br />
<b>Configure the Linksys WRT-45G:</b><br />
Go to Setup - Basic Setup<br />
<br />
In the WAN Setup area:<br />
Connection Type: Static IP<br />
WAN IP Address: 192.168.2.2<br />
Subnet Mask: 255.255.255.0<br />
Gateway: 192.168.2.1<br />
Static DNS 1: 8.8.8.8<br />
Static DNS 2: 4.2.2.1<br />
<br />
In the Network Setup area:<br />
Local IP Address: 192.168.4.1<br />
Subnet Mask: 255.255.255.0<br />
Gateway: 192.168.4.1<br />
Disable DHCP<br />
<br />
Go to Setup - Advanced Routing<br />
Set Operating Mode = Router<br />
<br />
Add 2 static routes, one to route wifi traffic to the ethernet network and one to go the other way:<br />
<br />
Route Name: wifi to ethernet<br />
Destination LAN NET: 192.168.2.0<br />
Subnet Mask: 255.255.255.0<br />
Gateway: 192.168.2.1<br />
Interface: LAN & WAN<br />
<br />
Route Name: ethernet to wifi<br />
Destination LAN NET: 192.168.86.0<br />
Subnet Mask: 255.255.255.0<br />
Gateway: 192.168.4.2<br />
Interface: LAN & WAN<br />
<div>
<br /></div>
Go to Administration - Commands, paste in this command:<br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">iptables -I FORWARD -j ACCEPT </span></span><br />
<br />
<span style="font-size: small;">Click "Run Commands", wait for it to finish, then click "Save Firewall" so this setting persists after reboot.</span><br />
<br />
I also disabled wireless since the Google pucks will handle that.<br />
<br />
<b>Configure the Google Wifi:</b><br />
On the main wifi puck, disconnect the ethernet cable.<br />
Open the Google Wifi app on your phone.<br />
Go to the third tab, then Network & General, Advanced Networking, then WAN.<br />
Under WAN Settings, select Static, then fill in:<br />
IP address: 192.168.4.2<br />
Subnet mask: 255.255.255.0<br />
Default gateway: 192.168.4.1 (this is the IP address of the Linksys)<br />
Save.<br />
If you get the "You cannot edit these settings" message, follow the "Show me how" instructions to do the above.<br />
<br />
<b>Wiring:</b><br />
Connect an ethernet cable from the WAN port on the Linksys to any open LAN port on the Archer C7.<br />
Connect the ethernet cable from the main Google Wifi puck to any open LAN port on the Linksys.<br />
<br />
Hopefully, everything works.<br />
<br />
<br /></div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-22402757534171145622018-09-16T16:29:00.001-07:002018-09-16T16:31:24.954-07:00How to control Kodi from Google Home with IFTTTMy last three posts have been about getting Google Home set up to control my stereo system. I have one more post to do with will concern getting my speaker controller to work with Google Home, which is a piece of cake after doing everything else.<br />
<br />
This is a short post, all the details are here:<br />
<br />
<a href="https://github.com/OmerTu/GoogleHomeKodi">https://github.com/OmerTu/GoogleHomeKodi</a><br />
<br />
Many, many thanks to OmerTu, I was seriously considering implementing the Kodi json-rpc api myself, which would be a lot of work, but then I found OmerTu has already done it.Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-84315134466844684772018-09-16T16:17:00.002-07:002018-09-16T16:22:45.146-07:00Controlling Yamaha RX-V673 via web requestsSo my last two posts have to do with setting up stuff so that I can use my new Google Home Mini's to control my stereo system. This post has to do with setting up a python webserver on my Kodi box so I can hit it from IFTTT and "forward" the requests to my amplifier. I have a Yamaha RX-X673, which has a web interface. I opened the web interface in my browser with http://ip-address, then the web interface appears. It's reasonably complete and looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioQ5J3nzIv0-NxmDTJxqEbEs73Omh2Bsl2X5OqPcrrvg_65vFiW9_7yChKyuBltmcdDQ8769gZa4wYyt9MQvAMbNEMju2H2idR41gpyRh7i_lTK92fH6cPfsTSWzH09UYK-jhh5wIiFQc/s1600/RX-V673.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="603" data-original-width="803" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioQ5J3nzIv0-NxmDTJxqEbEs73Omh2Bsl2X5OqPcrrvg_65vFiW9_7yChKyuBltmcdDQ8769gZa4wYyt9MQvAMbNEMju2H2idR41gpyRh7i_lTK92fH6cPfsTSWzH09UYK-jhh5wIiFQc/s640/RX-V673.png" width="640" /></a></div>
<br />
Then I fired up <a href="https://www.wireshark.org/" target="_blank">Wireshark</a> and captured the requests between my browser and the receiver. It turns out some idiot thought sending xml, rather than json, or even just regular key=value pairs via post, was a great idea. So, yeah, my amplifier has an xml parser built in. Who would have thought? It turns out that text/xml is not a content-type that I can use with IFTTT Maker Webhooks, unless I've overlooked something.<br />
<br />
Darn. This led me to set up a webserver on my Kodi box to take a request and then run a script per command to make the amplifier do its thing.<br />
<br />
Setting up a webserver on Kodi is surprisingly easy, simply create a cgi-bin directory on it. Just ssh to it:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ssh root@ip-address</span><br />
<br />
You'll end up in /storage by default, which is the right place to be.<br />
<br />
Oh, wait, I must mention that I'm running <a href="https://openelec.tv/" target="_blank">openelec</a> on my Kodi box. It's a dedicated box that sits inside my stereo cabinet. The default root password is "openelec" -- mine is not that anymore :)<br />
<br />
I created a directory named "webserver", although this isn't strictly necessary. What is necessary is to create a directory named "cgi-bin", which I put inside the webserver directory. This is the place to put python scripts to execute.<br />
<br />
The webserver itself is built into python, just<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">cd webserver</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">python -m CGIHTTPServer 8000</span><br />
<br />
This starts a webserver at port 8000 and will run scripts contained in the cgi-bin directory. I set a port forward on my router so the IFTTT generated requests will be sent to that port on my Kodi box.<br />
<br />
The scripts themselves are super simple, it's just does a <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">wget</span> call and returns a blank html page (which isn't at all necessary), so they look like this:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">#!/usr/bin/env python</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">import subprocess</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">subprocess.call(['wget', '--post-data=<YAMAHA_AV cmd="PUT"><System><Power_Control><Power>On</Power></Power_Control></System></YAMAHA_AV>', 'http://ip-address/YamahaRemoteControl/ctrl'])</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">print("Content-type: text/html")</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">print("")</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">print("""<html><body></body></html>""")</span><br />
<div>
<br /></div>
<div>
This particular script will turn on the amplifier, and change the "ip-address" to what is appropriate.</div>
<div>
<br /></div>
<div>
All that is required to run other commands is to replace the <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">--post-data</span> with a different xml. Here are the ones that I used:</div>
<br />
Power on:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><YAMAHA_AV cmd="PUT"><System><Power_Control><Power>On</Power></Power_Control></System></YAMAHA_AV></span><br />
<br />
Power off (actually standby):<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><YAMAHA_AV cmd="PUT"><System><Power_Control><Power>Standby</Power></Power_Control></System></YAMAHA_AV></span><br />
<br />
Change input, change HDMI1 to what you need, I made multiple scripts to do this (HDMI1, HDMI2, AV1, etc), althought I'm going to go back and add some logic based on a text value passed from IFTTT eventually:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><YAMAHA_AV cmd="PUT"><Main_Zone><Input><Input_Sel>HDMI1</Input_Sel></Input></Main_Zone></YAMAHA_AV></span><br />
<br />
Set volume, this one is a little different in that I have IFTTT sending a volume value. I want to be able to also have a "volume up" and "volume down" command. For now, I'm using this script, in which I'll receive a "volume" query parameter as a number:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">import subprocess</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">import cgi</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">import cgitb</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">cgitb.enable()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">form = cgi.FieldStorage()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">volume = form["volume"].value</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"># the minus before the volume is important, the amplifier has a volume range of</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"># -80.0 dB to 16.5 db, anything above 0 is way too loud for the house</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">command = '--post-data=<YAMAHA_AV cmd=\"PUT\"><Main_Zone><Volume><Lvl><Val>-' + volume + '</Val><Exp>1</Exp><Unit>dB</Unit></Lvl></Volume></Main_Zone></YAMAHA_AV>'</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">subprocess.call(['wget', command, 'http://ip-address/YamahaRemoteControl/ctrl'])</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">print("Content-type: text/html")</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">print("")</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">print("<html><body>")</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">print("</body></html>")</span><br />
<div>
<br /></div>
<div>
Note the "-" sign before the volume number, the actual range of volume on the RX-V673 is -80.0 dB to 16.5 dB. Anything bigger than about -5 is way too loud, the usual range, for me at least, is in the -15 to -30 dB range. </div>
<div>
<br /></div>
<div>
Mute on and off:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><YAMAHA_AV cmd="PUT"> <Main_Zone> <Volume> <Mute>On</Mute> </Volume> </Main_Zone> </YAMAHA_AV>'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><YAMAHA_AV cmd="PUT"> <Main_Zone> <Volume> <Mute>Off</Mute> </Volume> </Main_Zone> </YAMAHA_AV>'</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: inherit;">That's it for now. I'll update this when I get the volume up and down commands working.</span></div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-8910796796420306222018-09-07T17:28:00.001-07:002018-09-27T17:47:33.767-07:00How to install node.js on openelec KodiYet another short how to, I need node.js installed on my OpenElec Kodi box so I can get Google Home to control it. It took a bit of googling and some file editing.<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ssh root@box ip</span><br />
<br />
Should be in /storage<br />
<br />
Run:<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash</span><br />
<br />
It will produce a message about adding some lines to .profile instead of .bashrc, that's okay. Check /storage/.profile, it should have a couple of lines like this:<br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">export NVM_DIR="$HOME/.nvm"</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm</span><br />
<div>
<br /></div>
<div>
Log out, then ssh back to your Openelec box, this will force the new addition to .profile to be used.</div>
<div>
<br /></div>
<div>
Next, fix the nvm script so it works on Openelec.</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">cd .nvm</span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">nano nvm.sh</span></div>
<div>
Do a "Where Is" command in nano, search for " ls ", that's space, l, s, space, we're looking for an ls command. There are 3 of these:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">ls -1qA</span></div>
<div>
<br /></div>
<div>
Remove the 'q' from all 3, the busybox sh version of ls doesn't support the 'q' option, and that's okay too.</div>
<div>
<br /></div>
<div>
Now install node.js:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">nvm install node</span></div>
<div>
<br /></div>
<div>
This might take a minute or two.</div>
<div>
<br /></div>
<div>
Run the nvm "use" command:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">nvm use node</span></div>
<div>
<br /></div>
<div>
You should see something like:</div>
<div>
<br /></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Now using node v10.10.0 (npm v6.4.1)</span></div>
<div>
<br /></div>
<div>
Test that node is working:</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">node -v</span></div>
<div>
Output: <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">v10.10.0</span></div>
<div>
<br /></div>
<div>
Test that npm (node package manager) works</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">npm -v</span></div>
<div>
Output: <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">6.4.1</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span style="font-family: inherit;"><span style="font-size: small;">Update, 27 Sep 18: </span></span></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span style="font-family: inherit;"><span style="font-size: small;">I should have mentioned that I added this to my autostart.sh script:</span></span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span style="font-family: inherit;"><span style="font-size: small;">cd .config</span></span></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span style="font-family: inherit;"><span style="font-size: small;">nano autostart.sh</span></span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span style="font-family: inherit;"><span style="font-size: small;">Add this line:</span></span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">(sleep5;cd /storage/node/;nohup /storage/.nvm/versions/node/v10.10.0/bin/node /s<br />torage/node/server.js >> /storage/node/log.txt &) &</span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><span style="font-size: small;"><span style="font-family: inherit;">The 'sleep' waits a few seconds for the previous command to finish, 'nohup' lets the command continue to run uninterrupted, then there is the actually command with the full path or it won't work even though it's in the path, then the 'server.js' is the configuration, and last output to a log file. </span></span></span></div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com1tag:blogger.com,1999:blog-6172369976935919.post-35932643183774944532018-09-07T16:16:00.000-07:002018-09-27T17:42:54.155-07:00How to set up no-ip.com dynamic dns on openelec KodiGoogle didn't find and answer for this question, instead I got a lot of hits about installing the no-ip duc (dynamic update client) on Raspberry Pi. I want this on my Kodi box so I can get my Google Home devices to control Kodi. The first step is getting a dynamic dns going so the IFTTT servers can find me. Here are the details:<br />
<br />
First, set up a free account at noip.com. All you need is a valid email address, a password, and a domain name that you can make up on the spot. Write those down for later use.<br />
<br />
Next, install the no-ip duc client on the Kodi box:<br />
<br />
<ol>
<li>ssh root@ip of your Kodi, should be in /storage by default</li>
<li>mkdir noip</li>
<li>cd noip</li>
<li>download the client: wget http://www.no-ip.com/client/linux/noip-duc-linux.tar.gz</li>
<li>unzip it: tar -xvzf noip-duc-linux.tar.gz</li>
<li>cd /storage/scripts</li>
<li>copy the binary to the scripts directory: cp /storage/noip/noip-2.1.9-1/binaries/noip2-x86_64 .</li>
<li>create the configuration file: ./no-ip2-x86-64 -C -c ./no-ip2.conf</li>
<li>answer the questions</li>
</ol>
<div>
You can now delete the /storage/no-ip directory if you want.</div>
<div>
<br /></div>
<div>
Start the no-ip client:</div>
<br />
<div>
<ol>
<li>noip2-x86_64 -c /storage/scripts/no-ip2.conf</li>
<li>check that it's running: ps ax | grep noip</li>
</ol>
</div>
<div>
<div>
Set up auto start so no-ip client starts when Kodi starts:</div>
<div>
<ol>
<li>cd /storage/.config</li>
<li>nano autostart.sh</li>
<li>between the brackets ( and ) add: cd /storage/scripts;noip2-x86_64 -c /storage/scripts/no-ip2.conf</li>
</ol>
</div>
</div>
<div>
Configure your router to forward port 8245 TCP in both directions.</div>
<div>
<br /></div>
<div>
Check that it works by pinging the domain name you set up on the noip.com website:</div>
<div>
<br /></div>
<div>
ping myname.hopto.org</div>
<div>
<br /></div>
<div>
You should get a response.<br />
<br />
27 Sep 18, An update:<br />
I've had some trouble with the autostart.sh script, mostly because things don't run as they should. It turns out that full paths are required, and each command should be by itself. So for this, I added this line, all by itself, in autostart.sh:<br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">(sleep 30;nohup /storage/scripts/noip2-x86_64 -c /storage/scripts/no-ip2.conf &) &</span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">The 'sleep' makes the script wait for a bit so things before it actually run, 'nohup' makes the command run without hanging up, the '-c' parameter has the full path to the configuration for noip.</span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;">I wish they'd be consistent with noip or no-ip, I don't care which, but using both is a little confusing.</span><br />
<span style="font-family: "Courier New", Courier, monospace;"> </span><br />
</div>
<div>
<br /></div>
<br />
<div>
<br /></div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-13454791304054556502017-11-26T16:53:00.000-08:002018-09-16T16:44:09.568-07:00Set up Linksys WRT45GL as router with CenturyLink DSL and C1000A modemUpdated: I installed DD-WRT on my Linksys, so modified instructions below. "Linksys" means stock firmware, "DD-WRT" means DD-WRT firmware. The differences are very minor.<br />
<br />
Yet another update: my wife saw a bunch of google stuff at Costco, she bought a set of 3 google home minis and a set of 4 google wifi pucks. So the Linksys is gone.<br />
<br />
And one more update: I tried using the Google WiFi as my main house router, and it sucks. Port forwarding simply does not work. Instead, I did what I should have done in the first place -- I set the C1000A in to transparent bridge mode as described below, put my most capable router, a TP-Link Archer C7, as the main router on the 192.168.22.x network (also pretty much as described below), let Google set itself on the 192.168.86.x network (which it insists on doing), and added a route on the C7 so the two networks can talk to each other. Now I have the nice mesh wifi network from Google, all my hard-wired machines can talk to the wifi devices and vice versa, and I have a decent, fully configurable router.<br />
<br />
-------<br />
<br />
I've been having some trouble with CenturyLink and Private Internet Access VPN, the CenturyLink C1000A can't seem to handle all the connections so the VPN craps out regularly. The reviews of the C1000A are decent as a modem, not so good as a router. I have a Linksys WRT45GL, which has a good reputation as a router, so I've reconfigured my network to just use the C1000A as a modem and the Linksys as the router/gateway. It's pretty straightforward:<br />
<br />
<b>Set up the C1000A:</b><br />
<br />
Get username and password for CenturyLink account. If you don't know these (they should have been given to you when you set up the CenturyLink account), you can telnet to the modem and get them as follows:<br />
<br />
1. Log into C1000A with your browser.<br />
2. Go to Advanced Setup and then Remote Console (on the left nav) underneath Remote Management. On the Remote Console screen choose the "Telnet Enabled" in the "Select the Console State Below" dropdown menu. Set a username and password, write it down. Click the "Apply" button.<br />
3. Telnet to C1000A. Type "sh" to enter a shell. Type "/usr/bin/pidstat -l -C pppd" and hit Enter. Your username and password will be in the output. The password follows the "-p" in the output. Write these down for future reference. Quit your telnet session.<br />
<br />
<i>Note: pidstat is used for monitoring individual tasks currently being managed by the Linux kernel. The "-l" option displays the process command name and all its arguments. The "-C pppd" option filters the output to show only the processes named "pppd".</i><br />
<br />
Log into to C1000A with your browser.<br />
<br />
Go to wireless settings. Turn off wifi.<br />
<br />
Go to Advanced Settings, then WAN settings, find ISP Protocol and set it to "Transparent Bridge". Click the "Apply" button. Your internet connection will stop working for now.<br />
<br />
<br />
<b>Set up the Linksys WRT45GL:</b><br />
<br />
Log into Linksys with your browser.<br />
<br />
Linksys: Go to Setup, Basic Setup. Set "Internet Connection Type" to PPPoE. Enter your CenturyLink username and password. Set up DHCP if needed on this page since the C1000A will no longer be providing that service.<br />
<br />
DD-WRT: Go to Setup, Basic Setup. In "WAN Setup" set "WAN Connection Type" to PPPoE. Enter your CenturyLink username and password. Set up DHCP if needed on this page since the C1000A will no longer be providing that service.<br />
<div>
<br /></div>
Go to Setup, Advanced Routing. Set "Operating Mode" to Gateway. Save.<br />
<br />
<br />
<b>Redo the wiring:</b><br />
<br />
Unplug any ethernet cables going to the C1000A, but don't unplug the phone line since that is the DSL connection. Plug those cables into the Linksys. Connect an ethernet cable from the WAN port on the Linksys to one of the ethernet ports on the C1000A.<br />
<br />
<br />
<b>Restart both boxes:</b><br />
<br />
Unplug C1000A. Unplug Linksys. Wait 1 minute.<br />
<br />
Plug in the C1000A. Wait for the connection light to stop blinking (it looks like an outline of an ethernet port). This can take some time, but should be less than 5 minutes.<br />
<br />
Plug in the Linksys. Log into it with your browser. Go to the Status page. Check that it's connected and has an IP address.<br />
<br />
That is all. Assuming all your devices use DHCP, they should all automatically reconnect with the Linksys as the gateway.<br />
<br />
<br />
<div>
<br /></div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-21575209420181436962017-10-26T11:17:00.000-07:002017-10-26T11:17:04.793-07:00DVD rippingWe have a lot of DVDs, and we travel a lot. We usually pack along a hundred or so DVDs to play, but we also have a 1 TB portable hard drive that would easily hold all of those DVDs. I started ripping them with dvd::rip, which is okay, but takes forever. I looked around for other alternatives, but didn't find any that worked any better. I looked into some command line utilities, and strung together a little script that works like a charm, super easy and reasonably fast. This requires lsdvd (list dvd contents), mplayer, and ffmpeg. The script parses the output of lsdvd to get the track number of the longest title, passes that to mplayer to rip it, then ffmpeg takes the output of mplayer and transcodes it to H264 while retaining all language streams. Subtitles are not carried over because the subtitles from the dvd are in dvdsub format, which ffmpeg can't handle. Eventually, I plan to add another step in this to have <a href="http://www.mplayerhq.hu/DOCS/HTML/en/menc-feat-extractsub.html">mencoder pull the subtitles and convert them to VOBsub</a>, which ffmpeg can handle. Most of our DVDs are already in English, so there isn't much need for subtitles, but we do have a few foreign language films that could use the subtitles.<br />
<br />
Here's the script, as I said super simple, and pretty fast. There is a little set up, but that is spelled out below.<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">#!/bin/bash</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># usage: ripdvd "name of movie"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># eg: ripdvd "Tucker and Dale vs Evil"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">#</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># requires lsdvd, mplayer, and ffmpeg</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># output directory must already exist</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">VIDEOS=~/videos</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">cd $VIDEOS</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># title to use for movie from command line</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">TITLE=$1</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">FILENAME="$TITLE.mpg"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># lsdvd lists video tracks and the longest track is listed last</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">LSDVD_OUTPUT="$(lsdvd)"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">TRACK=`expr "$LSDVD_OUTPUT" : '.*\(..\)'`</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">#echo $LSDVD_OUTPUT</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># print out some stuff</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "Creating movie " $TITLE " from track " $TRACK</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "Output to " "$VIDEOS/$TITLE.mkv"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "Ripping..."</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># mplayer dumpstream outputs the given video track with all audio </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># and subtitle channels</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">mplayer -dumpstream dvd://$TRACK -nocache -noidx -dumpfile "$FILENAME"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "Transcoding..."</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "**************************************************************"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># read $TITLE.mpg, transcode all audio to vorbis, don't copy subtitle streams, </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># transcode the video to H264, output to matroska container</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># ultrafast takes 15-45 minutes depending on movie length. </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># Output file will be about 1.5 GB.</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># -sn prevents subtitle copying, which ffmpeg can't handle directly from the mpg</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">ffmpeg -i "$TITLE.mpg" -sn -map 0 -preset ultrafast "$TITLE.mkv"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># clean up, just delete the mpg file since it's no longer needed and can be </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"># quite large</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">rm "$TITLE.mpg"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">echo "Done!"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">exit</span><br />
<div>
<br /></div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com0tag:blogger.com,1999:blog-6172369976935919.post-89240944471529450892017-05-05T11:26:00.001-07:002017-05-05T11:34:01.013-07:00Speaker Controller, version 2, part 3, the codeHere is the code for the speaker controller. There are enough comments that I think it should be self-explanatory.<br />
<br />
<a href="https://drive.google.com/open?id=0B2u1gHmsRHhqTUs1ZGlrbUUwbDg">Download ESP01RelayServer.ino</a><br />
<br />
<br />
<div>
<style scoped="" type="text/css">
.syntax-NULL {
color: #dcdccc;
}
.syntax-COMMENT1 {
color: #7fb47f;
font-style: italic;
}
.syntax-COMMENT2 {
color: #7fb47f;
font-style: italic;
}
.syntax-COMMENT3 {
color: #7fb47f;
font-style: italic;
}
.syntax-COMMENT4 {
color: #7fb47f;
}
.syntax-DIGIT {
color: #8cd0d3;
}
.syntax-FUNCTION {
color: #efef8f;
font-weight: bold;
}
.syntax-INVALID {
background: #ffffcc;
color: #ff0066;
}
.syntax-KEYWORD1 {
color: #f0dfaf;
font-weight: bold;
}
.syntax-KEYWORD2 {
color: #dddd66;
font-weight: bold;
}
.syntax-KEYWORD3 {
color: #cccc66;
font-weight: bold;
}
.syntax-KEYWORD4 {
color: #9999ff;
font-weight: bold;
}
.syntax-LABEL {
color: #dfcfaf;
}
.syntax-LITERAL1 {
color: #cc9393;
}
.syntax-LITERAL2 {
color: #dca3a3;
font-weight: bold;
}
.syntax-LITERAL3 {
color: #f0dfaf;
}
.syntax-LITERAL4 {
color: #f0dfaf;
}
.syntax-MARKUP {
color: #c0c0c0;
}
.syntax-OPERATOR {
color: #f0efd0;
}
</style>
<br />
<pre><span class="syntax0"></span><span class="syntax-COMMENT1">/*
MIT License
Copyright (c) 2017 Dale Anson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
Revised speaker controller code to work with ESP-01 with extended outputs via
an MCP23017.
This is intended for controlling 8 speaker zones. It should be fairly straight-
forward to modify this to run as few as 2 or as many as 128 zones.
The hardware is constructed with two 8 relay modules. One module is used for the
left speakers, the other for the right. Doing it this way makes the code cleaner
in setting the bits for each module since the bit field is the same for each.
There is no real security here, passwords are stored unencrypted in flash memory.
It is NOT recommended to connect the speaker controller directly to the internet,
rather, it should be behind a firewall. Nothing in this code calls the internet,
but I can't claim the same for the included libraries. I don't think they do, but
I haven't confirmed.
*/
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><ArduinoJson.h></span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// read/write config file
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><EEPROM.h></span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// to read wifi credentials
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><ESP8266HTTPUpdateServer.h></span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// for ota update
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><ESP8266WebServer.h>
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><ESP8266WiFi.h>
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><ESP8266mDNS.h></span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// dns server
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><FS.h></span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// file system
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><WiFiClient.h>
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><WiFiManager.h>
</span><span class="syntax-KEYWORD2">#</span><span class="syntax-MARKUP">include</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1"><Wire.h></span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// i2c comms
// i2c address of mcp23017 chip, the chip is hardwired to this address, so don't
// change it unless you rewire the chip
</span><span class="syntax-KEYWORD1">const</span><span class="syntax-NULL"> byte mcp_address </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0x20</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// mcp23017 address of bank A
</span><span class="syntax-KEYWORD1">const</span><span class="syntax-NULL"> byte GPIOA </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0x12</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// mcp23017 address of bank B
</span><span class="syntax-KEYWORD1">const</span><span class="syntax-NULL"> byte GPIOB </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0x13</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// storage for access point password
</span><span class="syntax-NULL">String apPassword </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"speakercontroller"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// default speaker zone names
</span><span class="syntax-NULL">String zoneNames</span><span class="syntax-OPERATOR">[]</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{</span><span class="syntax-LITERAL1">"Zone 0"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone 1"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone 2"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone 3"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone 4"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone 5"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone 6"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone 7"</span><span class="syntax-OPERATOR">};
</span><span class="syntax-COMMENT1">// number of zones
</span><span class="syntax-KEYWORD3">int</span><span class="syntax-NULL"> zones </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">8</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// flag indicating configuration needs to be saved
</span><span class="syntax-KEYWORD3">bool</span><span class="syntax-NULL"> shouldSaveConfig </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL2">false</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// web server runs on port 80, http only
</span><span class="syntax-NULL">ESP8266WebServer </span><span class="syntax-FUNCTION">server</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">80</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL">ESP8266HTTPUpdateServer updater</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// bit field for zone state, initially all are off -- note that for relays, 1 means
// means off, 0 means on
</span><span class="syntax-NULL">byte zoneState </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0b11111111</span><span class="syntax-OPERATOR">;
</span><span class="syntax-COMMENT1">// for over-the-air (ota) updates and local dns
</span><span class="syntax-KEYWORD1">const</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD3">char</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">*</span><span class="syntax-NULL">host </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"speakercontroller"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-KEYWORD1">const</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD3">char</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">*</span><span class="syntax-NULL">updatePath </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"/firmware/update"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-KEYWORD1">const</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD3">char</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">*</span><span class="syntax-NULL">updateUsername </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"admin"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setup</span><span class="syntax-OPERATOR">(</span><span class="syntax-KEYWORD1">void</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// initialize the wire library, use GPIO0 and GPIO2 pins on ESP-01
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// for SDA and SCL, respectively. This sets up comms with the MCP23017.
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">begin</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">2</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// serial monitor port
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">begin</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">115200</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">""</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"Speaker Controller starting"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// load configuration, if any. This also initializes the file system.
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">loadConfig</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// turn off all relays at start up
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"initialize relays..."</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">initialize</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"relays initialized"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// for testing
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// resetServer();
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// start dns, this speaker controller can be found at http://speakercontroller.local
</span><span class="syntax-NULL"> MDNS</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">begin</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">host</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set up wifi
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWifi</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">false</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set up web server
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWebServer</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">false</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">loop</span><span class="syntax-OPERATOR">(</span><span class="syntax-KEYWORD1">void</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">handleClient</span><span class="syntax-OPERATOR">();</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-COMMENT1">// initialize the i2c communications. On first call, this will turn off all the
// relays since zoneState will be all off. Then on restarts, the speaker
// state will be recovered from flash memory and used to set them as they were
// before the restart. This is nice for situations like when the power blinks.
//
// NOTE: I put this code here because comms between the esp-01 and mcp23017 is a
// little flaky on the breadboard and I can re-initialize easily on error. There
// is no flakiness when everything is soldered to a prototype board.
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">initialize</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// loop until connected with MCP23017
</span><span class="syntax-NULL"> byte error</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">while</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"initialization error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error </span><span class="syntax-OPERATOR">==</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">break</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">delay</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">1000</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set MCP23017 GPAx pins to output
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">((</span><span class="syntax-NULL">byte</span><span class="syntax-OPERATOR">)</span><span class="syntax-DIGIT">0x00</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// IODIRA register
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">((</span><span class="syntax-NULL">byte</span><span class="syntax-OPERATOR">)</span><span class="syntax-DIGIT">0x00</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set all of bank A to outputs
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set MCP23017 GPBx pins to output
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">((</span><span class="syntax-NULL">byte</span><span class="syntax-OPERATOR">)</span><span class="syntax-DIGIT">0x01</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// IODIRB register
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">((</span><span class="syntax-NULL">byte</span><span class="syntax-OPERATOR">)</span><span class="syntax-DIGIT">0x00</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set all of bank B to outputs
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set the left speaker relays
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOA</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set the right speaker relays
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOB</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
}
</span><span class="syntax-COMMENT1">// set up the wifi. if 'reset' is true, reset all stored wifi parameters, this
// is useful for being able to get to the AP again to be able to attach to a
// new wifi network
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWifi</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">boolean reset</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set up WiFiManager
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"set up wifi"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> WiFiManager wifiManager</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// reset saved settings, this clears out previously saved ssid and pw and
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// any other stored wifi parameters
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">reset</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"resetting wifi"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> wifiManager</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">resetSettings</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// allow user to change the access point password
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// TODO: finish this, it allows the user to change the access point password
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">//WiFiManagerParameter custom_ap_password("apPassword", "AP Password", apPassword.c_str(), 32);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">//wifiManager.addParameter(&custom_ap_password);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set up the config callback, The wifiManager will call this when it determines
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// the configuration has been changed.
</span><span class="syntax-NULL"> wifiManager</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">setSaveConfigCallback</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">saveConfigCallback</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// fetches ssid and pass from eeprom and tries to connect
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// if it does not connect it starts an access point with the specified name
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// and goes into a blocking loop awaiting configuration. the access point
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// should show up in the network manager of a phone or computer, it should
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// have an IP address of 192.168.4.1. This lets the user choose which wifi
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// network to connect to.
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"apPassword = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">apPassword</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(!</span><span class="syntax-NULL">wifiManager</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">autoConnect</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"SpeakerControllerAP"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> apPassword</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">c_str</span><span class="syntax-OPERATOR">()))</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"wifi failed to connect, resetting to try again"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">delay</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">3000</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> ESP</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">reset</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">delay</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">5000</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// possibly save the wifi configuration
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">shouldSaveConfig</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">saveConfig</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// print out connection details to serial monitor
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"wifi connected, details follow:"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">print</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"Connected to SSID: "</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">WiFi</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">SSID</span><span class="syntax-OPERATOR">());
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">print</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"IP address: "</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">WiFi</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">localIP</span><span class="syntax-OPERATOR">());
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">print</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"MAC address: "</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> byte mac</span><span class="syntax-OPERATOR">[</span><span class="syntax-DIGIT">6</span><span class="syntax-OPERATOR">];
</span><span class="syntax-NULL"> WiFi</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">macAddress</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mac</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-FUNCTION">getMacString</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mac</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">print</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"Gateway: "</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">WiFi</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">gatewayIP</span><span class="syntax-OPERATOR">());
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">print</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"DNS: "</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">WiFi</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">dnsIP</span><span class="syntax-OPERATOR">());
}
</span><span class="syntax-COMMENT1">// callback notifying us of the need to save config
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">saveConfigCallback</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"should save config"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> shouldSaveConfig </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-COMMENT1">// set up the urls that this server will recognize
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWebServer</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">boolean reset</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// maybe stop the web server
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">reset</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"stopping web server"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">stop</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"web server stopped"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// define the url handlers
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"starting web server"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> handleRoot</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> handleRoot</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/state"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> handleState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/all/on"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">[]()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleAll</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">});
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/all/off"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">[]()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleAll</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">false</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">});
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/config"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> handleConfigForm</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/zoneconfig"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> handleZoneConfigForm</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/zoneconfig/save"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> handleZoneConfigSave</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/server/reset/confirmwifi"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> confirmResetWifi</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/server/reset/confirmall"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> confirmFactoryReset</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/server/reset/wifi"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> resetWifi</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/server/reset/all"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> resetServer</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">on</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/speaker/server/update"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> flash</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">onNotFound</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">handleRoot</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL">
</span><span class="syntax-COMMENT1">// attach update server to web server
</span><span class="syntax-NULL"> updater</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">setup</span><span class="syntax-OPERATOR">(&</span><span class="syntax-NULL">server</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> updatePath</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> updateUsername</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> apPassword</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">c_str</span><span class="syntax-OPERATOR">());</span><span class="syntax-NULL">
</span><span class="syntax-COMMENT1">// start the web servver
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">begin</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL">
</span><span class="syntax-COMMENT1">// update dns service discovery to include the web server
</span><span class="syntax-NULL"> MDNS</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">addService</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"http"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"tcp"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">80</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"web server started"</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-COMMENT1">// this method is called when a request is made to the root of the server. i.e.
// http://speakercontroller.local/, or on any bad url that would usually cause a 404
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleRoot</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">sendPage</span><span class="syntax-OPERATOR">();</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-COMMENT1">// this method is called when the url is /speaker/state. There should be a
// query string something like 1=on&2=on&3=on&4=on, where the number is a zone
// number and the value is 'on'. All that is necessary here is to
// extract the query string and check for each of the zone numbers. Note there
// may not be a query string, in which case, all relays are turned off.
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleState</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// turn off all relays
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">allOff</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// figure out what is in the request, there may not be any args at all.
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-KEYWORD3">int</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">args</span><span class="syntax-OPERATOR">();</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String argName </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">argName</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD3">int</span><span class="syntax-NULL"> zone </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> argName</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">toInt</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"turning on zone "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> zone</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zone </span><span class="syntax-OPERATOR">>=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">&&</span><span class="syntax-NULL"> zone </span><span class="syntax-OPERATOR"><=</span><span class="syntax-NULL"> zones</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> zoneState </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">bitWrite</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> zone</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set the left speaker relays
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"zoneState = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOA</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> byte error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"handleState, error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// set the right relays
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOB</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"handleState, error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">sendPage</span><span class="syntax-OPERATOR">();
}
</span><span class="syntax-COMMENT1">// turn off all relays
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">allOff</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> zoneState </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0b11111111</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// left speakers
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOA</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> byte error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// right speakers
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOB</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
}
</span><span class="syntax-COMMENT1">// this method is called when the url is /speaker/all/on or /speaker/all/off,
// this turns on or off all relays
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleAll</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">boolean state</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">state</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> zoneState </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0b00000000</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// turn on all left speakers
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOA</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> byte error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// turn on all right speakers
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">beginTransmission</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mcp_address</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">GPIOB</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> error </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> Wire</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">endTransmission</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"error = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">error</span><span class="syntax-OPERATOR">));
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">else</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">allOff</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">sendPage</span><span class="syntax-OPERATOR">();
}
</span><span class="syntax-COMMENT1">// this method is called when the url is /speaker/config, this sends the
// configuration page back to the browser
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleConfigForm</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String response </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getConfigForm</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">200</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/html"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> response</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-COMMENT1">// this method is called then the url is /speaker/zoneconfig, this sends the
// zone configuration page back to the browser
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleZoneConfigForm</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String response </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getZoneConfig</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">200</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/html"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> response</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-COMMENT1">// this method is called when the url is /speaker/zoneconfig/save. There should be a
// query string something like 1=Foo&2=Bar, where the number is a zone number and
// the value is the name to use for that zone.
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">handleZoneConfigSave</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">byte i </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> zones</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String argName </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> String value </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">arg</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">argName</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">value</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">length</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">></span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> zoneNames</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">]</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> value</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">saveConfig</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">sendPage</span><span class="syntax-OPERATOR">();
}
</span><span class="syntax-COMMENT1">// sends a confirmation page to the browser asking the user to confirm resetting
// the wifi parameters
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">confirmResetWifi</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String response </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getConfirmResetWifiForm</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">200</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/html"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> response</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-COMMENT1">// clears stored wifi configuration, user will need to reconfigure wifi to connect
// to SpeakerControllerAP to set up wifi again.
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">resetWifi</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"reset wifi"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> String response </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getResetWifiMessage</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">200</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/html"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> response</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWifi</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// 'true' resets wifi manager settings
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWebServer</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// 'true' stops and restarts the web server
</span><span class="syntax-OPERATOR">}
</span><span class="syntax-COMMENT1">// sends a confirmation page to the browser asking the user to confirm resetting
// all saved settings
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">confirmFactoryReset</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String response </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getConfirmFactoryResetForm</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">200</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/html"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> response</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-COMMENT1">// clears stored configuration and wifi settings, and restarts the web server
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">resetServer</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// send the reset message back to the browser
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"reset server"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> String response </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getResetWifiMessage</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">200</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/html"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> response</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL">
</span><span class="syntax-COMMENT1">// wipe eeprom storage, this is where the ssid and password are stored
</span><span class="syntax-NULL"> EEPROM</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">begin</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">255</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-KEYWORD3">int</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">255</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> EEPROM</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">write</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> EEPROM</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">end</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL">
</span><span class="syntax-COMMENT1">// wipe the file system, this is where json configuration is stored
</span><span class="syntax-NULL"> SPIFFS</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">format</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL">
</span><span class="syntax-COMMENT1">// reset wifi and webserver
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWifi</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// 'true' resets wifi manager settings
</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">setupWebServer</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// 'true' stops and restarts the web server
</span><span class="syntax-OPERATOR">}
</span><span class="syntax-COMMENT1">// this lets the user upload a new .bin file to replace the current running code.
// This doesn't quite work, the file uploads okay, but then the ESP-01 restarts and
// fails to run.
// TODO: fix it so it works. I think what needs to happen is the 'flash' button
// needs to be held down while uploading the new code... maybe?
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">flash</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// redirect to flash page
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"redirecting to flash page"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> WiFi</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">mode</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">WIFI_AP_STA</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> String url </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"http://"</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">host</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">".local"</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">updatePath</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"update url = "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> url</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">sendHeader</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"Location"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> url</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL2">true</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">302</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/plain"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">""</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-COMMENT1">// loads stored settings from the esp8266 file system, the configuration is
// stored as a json file.
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">loadConfig</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// for testing, this wipes the file system partition from the flash memory
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// SPIFFS.format();
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// read configuration from the file system, the configuratio file is named config.json
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"mounting FS..."</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">SPIFFS</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">begin</span><span class="syntax-OPERATOR">())</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"mounted file system"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">SPIFFS</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">exists</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/config.json"</span><span class="syntax-OPERATOR">))</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"reading config file"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> File configFile </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> SPIFFS</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">open</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/config.json"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"r"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">configFile</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"opened config file"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// allocate a buffer to store contents of the file
</span><span class="syntax-NULL"> size_t size </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> configFile</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">size</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> std</span><span class="syntax-OPERATOR">::</span><span class="syntax-NULL">unique_ptr</span><span class="syntax-OPERATOR"><</span><span class="syntax-KEYWORD3">char</span><span class="syntax-OPERATOR">[]></span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">buf</span><span class="syntax-OPERATOR">(</span><span class="syntax-KEYWORD1">new</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD3">char</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">size</span><span class="syntax-OPERATOR">]);
</span><span class="syntax-NULL"> configFile</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">readBytes</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">buf</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">get</span><span class="syntax-OPERATOR">(),</span><span class="syntax-NULL"> size</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// parse the json config file
</span><span class="syntax-NULL"> DynamicJsonBuffer jsonBuffer</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> JsonObject </span><span class="syntax-OPERATOR">&</span><span class="syntax-NULL">json </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> jsonBuffer</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">parseObject</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">buf</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">get</span><span class="syntax-OPERATOR">());
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">json</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">success</span><span class="syntax-OPERATOR">())</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">printTo</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">Serial</span><span class="syntax-OPERATOR">);</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// see contents in serial monitor
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"\nparsed json"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// read zone labels
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">byte i </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> zones</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String key </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"zone"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">json</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">containsKey</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">key</span><span class="syntax-OPERATOR">))</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> zoneNames</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">]</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">key</span><span class="syntax-OPERATOR">].</span><span class="syntax-FUNCTION">asString</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// read stored zone state, if any
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">json</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">containsKey</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"zonestate"</span><span class="syntax-OPERATOR">))</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> zoneState </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">[</span><span class="syntax-LITERAL1">"zonestate"</span><span class="syntax-OPERATOR">];
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// read access point password, if any
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">json</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">containsKey</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"apPassword"</span><span class="syntax-OPERATOR">))</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> apPassword </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">[</span><span class="syntax-LITERAL1">"apPassword"</span><span class="syntax-OPERATOR">].</span><span class="syntax-FUNCTION">asString</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">else</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"failed to load json config"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">else</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"config file not found"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">else</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"failed to mount FS"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
}
</span><span class="syntax-COMMENT1">// saves configuration to the esp8266 file system. The file is a json file
// named config.json
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">saveConfig</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"saving configuration"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> DynamicJsonBuffer jsonBuffer</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"create json object"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> JsonObject </span><span class="syntax-OPERATOR">&</span><span class="syntax-NULL">json </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> jsonBuffer</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">createObject</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// zone labels
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"add zone labels"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">byte i </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> zones</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String label </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"zone"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">label</span><span class="syntax-OPERATOR">]</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> zoneNames</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">];
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// zone state
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"add zone state"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">[</span><span class="syntax-LITERAL1">"zonestate"</span><span class="syntax-OPERATOR">]</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> zoneState</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// access point password
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"add access point password"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">[</span><span class="syntax-LITERAL1">"apPassword"</span><span class="syntax-OPERATOR">]</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> apPassword</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"open config.json"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> File configFile </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> SPIFFS</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">open</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"/config.json"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"w"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">if</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(!</span><span class="syntax-NULL">configFile</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"failed to open config file for writing"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"config file open, print to serial"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">printTo</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">Serial</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"\nconfig file open, print to file"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> json</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">printTo</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">configFile</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> Serial</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">println</span><span class="syntax-OPERATOR">(</span><span class="syntax-LITERAL1">"close file"</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> configFile</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">close</span><span class="syntax-OPERATOR">();
}
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getMacString</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">byte </span><span class="syntax-OPERATOR">*</span><span class="syntax-NULL">mac</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String result </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">""</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> String separator </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">""</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-KEYWORD3">int</span><span class="syntax-NULL"> b </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> b </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">6</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> b</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> result </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> separator </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">mac</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">b</span><span class="syntax-OPERATOR">],</span><span class="syntax-NULL"> HEX</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> separator </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">":"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> result</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-COMMENT1">// sends the main page to the browser
</span><span class="syntax-KEYWORD1">void</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">sendPage</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String response </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getForm</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> response </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">();
</span><span class="syntax-NULL"> server</span><span class="syntax-OPERATOR">.</span><span class="syntax-FUNCTION">send</span><span class="syntax-OPERATOR">(</span><span class="syntax-DIGIT">200</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"text/html"</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> response</span><span class="syntax-OPERATOR">);
}
</span><span class="syntax-COMMENT1">// the 'bootstrap' css makes the page look good in desktop browsers and on phones
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getHead</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<html>\n<head>\n<title>Speaker Control</title>\n<meta name='viewport' content='width=device-width, "
</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"initial-scale=1'>\n<link rel='stylesheet' "
</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>\n</head>\n<body>\n"</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-COMMENT1">// form to turn on and off the speaker zones
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getForm</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String message </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='container'>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<h2>Speaker Control</h2><br/>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<form action='/speaker/state' role='form'>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">byte i </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> zones</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD3">int</span><span class="syntax-NULL"> pinState </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">bitRead</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">zoneState</span><span class="syntax-OPERATOR">,</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">==</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">1</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">?</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">:</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">1</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='checkbox'><label><input type='checkbox' name='"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"'"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> pinState </span><span class="syntax-OPERATOR">==</span><span class="syntax-NULL"> HIGH </span><span class="syntax-OPERATOR">?</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">" checked='checked'"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">:</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">""</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">">"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> zoneNames</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">]</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"</label></div><br/>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// save button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<br/>\n<button type='submit' class='btn btn-primary'>Save</button>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// all on button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/all/on' role='button'>All On</a>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// all off button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/all/off' role='button'>All Off</a>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// config button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/config' role='button'>Config</a>\n</form>\n</div>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> message</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getTail</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"</body>\n</html>\n"</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getConfigForm</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String message </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='container'>\n<h2>Configuration Options</h2><br/>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<form role='form'>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// zone configuration button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/zoneconfig' role='button'>Zone Config</a>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// reset wifi button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/server/reset/confirmwifi' role='button'>Reset Wifi</a>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// reset all button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/server/reset/confirmall' role='button'>Factory Reset</a>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// flash button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/server/update' role='button'>OTA Flash</a></form>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL">
message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<p><a href='/'>Home</a>\n</div>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> message</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getConfirmResetWifiForm</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String message </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='container'>\n<h2>Confirm Reset Wifi</h2><br/>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<form role='form'>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// zone configuration button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/server/reset/wifi' role='button'>Reset Wifi</a>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// reset wifi button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/config' role='button'>Cancel</a></form></div>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> message</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getConfirmFactoryResetForm</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String message </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='container'>\n<h2>Confirm Factory Reset</h2><br/>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<form role='form'>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// zone configuration button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/server/reset/all' role='button'>Factory Reset</a>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-COMMENT1">// reset wifi button
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/config' role='button'>Cancel</a></form></div>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> message</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getResetWifiMessage</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String message </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='container'>\n<h2>Wifi is resetting!</h2><br/>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Use your network manager to connect to SpeakerControllerAP to configure the speaker controller to connect to a new wifi network.</div>"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> message</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getFactoryResetMessage</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String message </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='container'>\n<h2>SpeakerController is resetting!</h2><br/>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Use your network manager to connect to SpeakerControllerAP to configure the speaker controller to connect to a new wifi network.<br>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Once wifi is reconfigured, zone names will need to be reconfigured.</div>"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> message</span><span class="syntax-OPERATOR">;
}
</span><span class="syntax-COMMENT1">// form to configure the names of the speaker zones
</span><span class="syntax-NULL">String </span><span class="syntax-FUNCTION">getZoneConfig</span><span class="syntax-OPERATOR">()</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String message </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='container'>\n<h2>Zone Name Configuration</h2><br/>\n<form "
</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"action='/speaker/zoneconfig/save' role='form'>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">for</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">byte i </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-DIGIT">0</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i </span><span class="syntax-OPERATOR"><</span><span class="syntax-NULL"> zones</span><span class="syntax-OPERATOR">;</span><span class="syntax-NULL"> i</span><span class="syntax-OPERATOR">++)</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">{
</span><span class="syntax-NULL"> String index </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-FUNCTION">String</span><span class="syntax-OPERATOR">(</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">);
</span><span class="syntax-NULL"> String label </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"Zone "</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> index</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> String name </span><span class="syntax-OPERATOR">=</span><span class="syntax-NULL"> zoneNames</span><span class="syntax-OPERATOR">[</span><span class="syntax-NULL">i</span><span class="syntax-OPERATOR">];
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<div class='form-group'>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<label for='"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> index </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"'>"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> label </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"</label>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<input type='text' class='form-control' id='"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> index </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"' name='"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> index </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"' value='"</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">+</span><span class="syntax-NULL"> name </span><span class="syntax-OPERATOR">+
</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"'>\n</div>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-OPERATOR">}
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<br/>\n<button type='submit' class='btn btn-primary'>Save</button>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> message </span><span class="syntax-OPERATOR">+=</span><span class="syntax-NULL"> </span><span class="syntax-LITERAL1">"<a class='btn btn-primary' href='/speaker/config' role='button'>Cancel</a></form></div>\n"</span><span class="syntax-OPERATOR">;
</span><span class="syntax-NULL"> </span><span class="syntax-KEYWORD1">return</span><span class="syntax-NULL"> message</span><span class="syntax-OPERATOR">;
}
</span></pre>
</div>
Dale Ansonhttp://www.blogger.com/profile/13703878916409923767noreply@blogger.com6