Jekyll2021-03-16T15:57:41+00:00https://ofbrooklyn.com/feeds/all/OfBrooklyn.comSamuel ClayRecap and takeaways of the Micromobility panel at Moving the Future 20202020-02-06T04:20:00+00:002020-02-06T04:20:00+00:00https://ofbrooklyn.com/2020/02/6/micromobility-panel-moving-the-future<hr /> <p>I moderated a panel on micromobility at the annual <a href="http://mtfcon.org">Moving the Future Conference</a> at Harvard Business School. I had the fortune of discussing this decade’s favorite topic with four heavyweights in the industry. We had the panel discussion on Groundhog Day, when Punxsutawney Phil saw no shadow and thereby predicted an early spring (ain’t that the truth),</p> <p>We discussed the potential for micromobility, the cannibalization of walking and transit usage, aggregation of micromobility vehicles, and what the future of the vehicle looks like. Here’s all the good stuff and memorable lines from the 40 minute conversation.</p> <p><img src="/media/photos/micromobility-panel-2.jpg" width="500" /></p> <p>The micromobility panel includes, in order from left to right:</p> <ul> <li><span class="mm-name mm-matt "><a href="https://www.linkedin.com/in/mattwarfield">Matt Warfield</a></span>, a new mobility planner with the City of Boston Transportation Department</li> <li><span class="mm-name mm-tarani "><a href="https://twitter.com/TaraniDuncan">Tarani Duncan</a></span>, a product strategist and advisor to pioneering autonomous micromobility company Tortoise and spatial analytics company Zoba</li> <li><span class="mm-name mm-assaf "><a href="https://www.linkedin.com/in/assaf-biderman-751b135a">Assaf Biderman</a></span>, CEO and founder of Superpedestrian, a robotics company building the next wave of intelligent and connected micromobility vehicles</li> <li><span class="mm-name mm-andrew "><a href="https://twitter.com/andrewsalzberg">Andrew Salzberg</a></span>, former head of transportation policy and research at Uber and current Loeb fellow at the Graduate School of Design at Harvard</li> <li><span class="mm-name mm-sam "><a href="https://twitter.com/samuelclay">Samuel Clay</a></span>, moderator and graduate student in design engineering</li> </ul> <!--more--> <hr /> <p><strong>Do you see micromobility moving a meaningful chunk of drivers out of cars?</strong></p> <ul class="mm"> <li><span class="mm-andrew "></span> With cars, putting them on the road with phones was enough to create demand. But with bikes, putting thousands on the road doesn’t have the same effect because demand depends on the design of streets. Obvious to bike advocates but not so much to the rental fleet companies.</li> <li><span class="mm-tarani "></span> Operators are thought to posses all these tools to robustly calculate demand but that is not the case and this is a huge problem that needs to be solved. Spatial analytics are a huge part of making micromobility work as a tool for trip planning and that’s going to take time to build.</li> <li><span class="mm-assaf "></span> Multiple modalities is not a new solution to reducing vehicle miles traveled. Mass transit, from the bus, light rail, and subway, are really good at serving a certain portion of the trip demand, but what about people in the suburbs? Today you can only deploy where you can ensure demand, and that means downtowns and mainly where walking would have been the dominant mode. Mayors / city officials are saying that they’re not interested in micromobility for the less than 1 kilometer trips that saves rich people from walking. But now they’re working with operators to shape micromobility into the planning of the city and that’s a slow process.</li> <li><span class="mm-matt "></span> The city of Boston currently has no e-scooter operators. And that’s because the city does not yet see the added benefit of scooters relative to their risk. Ideally, we are providing options for people to forgo car trips, however, it has been shown that e-scooters take away ridership from public transit, walking, and using bike share. There needs to be a connection at the end to public transit in order to justify itself and the city is not seeing that happen.</li> <li><span class="mm-matt"></span> The city of Baltimore is starved for transportation options. Public transit is not necessarily reliable and not designed to move people through the city. Baltimore like many American cities is car centric. And even the bike share system in Baltimore wasn’t doing well. Bird then launched in the city and provided the impetus to come up with a policy to address micromobility. So the city of Baltimore shut down the shared docked bike system and ramped up the scooter pilot, which allowed the city to focus more on equity in terms of who was being served by each mode. This pilot was so successful due to the latent demand for transportation options.</li> </ul> <p><strong>Are micromobility operators doing enough to ensure a healthy future?</strong> Assuming micromobility continues growing at the rate it enjoys today, and with only so much walking it can cannibalize, what does transit ridership look like in the next few years?</p> <ul class="mm"> <li><span class="mm-andrew "></span> Having top line growth be the only concern has been a recipe for conflict with cities. Cities are reluctant to let something like micromobility grow as fast as it wants to without knowing what the consequences are. If the focus shifts to better management of fleet effectiveness, for example with the help of autonomous scooter company Tortoise (and others like the autonomous bicycle company Weel), the dynamic would then improve between operators and cities.</li> <li><span class="mm-andrew "></span> And the recent change in tone of the VC market, specifically the tightening of belts, might hasten be a turning point for these micromobility businesses. Cities should take this opportunity seriously or it may go away before long.</li> <li><span class="mm-assaf "></span> Looking at Seleta Reynolds and LADOT’s Mobility Data Specification (MDS), there’s a big opportunity for cities. Here’s an API for the city to define geofencing rules and not have to provide enforcement themselves. Let the city decide where to park, what should be the speed limit, and have the city be responsible. And if they’re wrong, let them worry about it and make changes to fix it.</li> <li><span class="mm-tarani "></span> Micromobility has been a trojan horse for conversations about how we allocate public space and for what ridership privacy means for data collection.</li> <li><span class="mm-tarani "></span> So the future of operators in the physical realm will include partnerships with cities in the form of mobility hubs and protected bike lanes. But with data there’s contention because MDS is de-identified but never truly anonymized. That means that with research people can be individually located in a dataset based on a few observations. Should cities have access to data at this level? Probably not but neither should micromobility operators.</li> <li><span class="mm-tarani "></span> We’re also seeing profound implications on the routing network. The city of Los Angeles is telling mapping companies to exclude certain roads and areas from routing directions, which has network effects on the road network. We’re seeing cities reining control.</li> <li><span class="mm-tarani "></span> The city of D.C. will subsidize public bike share programs. In a V.C. funded world there’s an approach of one company winning above others, but that is challenging when you consider how cities should be operating their own transit system.</li> <li><span class="mm-assaf "></span> Operators don’t need individual routes in order to interface with their data. It’s not a take it or leave it situation. What cities really want is to know the capacity of each mode moving in the city and that can be managed without individually identifying users.</li> <li><span class="mm-sam "></span> Important to watch not only how many cities take up MDS, but how many later shelve it.</li> <li><span class="mm-matt "></span> Thinking about the city of Boston, there’s a lot of vehicles in the city that are sitting unused. And with e-scooters, we’re probably going to convince some people to drop their cars. But e-scooters will not necessarily replace car trips in general and the impact for a city like Boston will be fairly low. But I think it will create opportunities for cities to do better things than just store vehicles on the road.</li> </ul> <p>Let’s go up the value chain and continue talking about data. We now have multiple aggregators of shared scooter/e-bike availability, fixing the problem of half a dozen apps on your phone. <strong>What is the role of the consumer interface for micromobility?</strong></p> <ul class="mm"> <li><span class="mm-andrew"></span> There’s this question of what is the most efficient use of vehicles. If you have multiple operators that don’t talk to each other, you end up with a surplus of vehicles, and this is due partially to the fact that we currently lack a way to connect operators together.</li> <li><span class="mm-andrew"></span> The conversation on data has mostly been about operator-to-regulator, in the form of MDS, but there’s also the importance of operator-to-consumer, in the form of how users access vehicles. There’s been a few dust-ups recently, one where <a href="https://ny.curbed.com/2019/9/30/20891536/nyc-transit-app-lyft-citi-bike">Lyft shut down Transit’s access to unlocking its bikes</a> and where <a href="https://www.cnet.com/news/scooter-app-wars-begin-and-lime-isnt-happy-about-it/">Lime shut down Scooter Map’s access to vehicle locations</a>.</li> <li><span class="mm-andrew"></span> This also becomes less of a question if competition goes away and there’s a single operator in a city, perhaps if the transit agency is running it. But if there is going to be a competitive market with multiple modes working together, then the question of how to integrate them and on what terms becomes important.</li> <li><span class="mm-assaf"></span> It’ll be a while before aggregators will be seen on the market, at least not until vehicles are an online asset in a massive sense, possibly with autonomy. But aggregation will really have an impact if the routing algorithms do a lot better job when travel time and vehicle miles traveled can be minimized, then we’ll have meaningful impact for operators.</li> <li><span class="mm-tarani"></span>Mobility-as-a-service (MaaS) has been such a buzz word but it’s not yet working. So aggregation of mobility platforms is a nice thought but if vehicles aren’t where you need them when you need them, then it doesn’t matter. The success of aggregators is less about displaying digital information and more about positioning the right vehicle at the right time. Similar to how a bus stop would be known to a potential customer, knowing that a vehicle is at or near a location when you need it is more important than seeing where they are now.</li> <li><span class="mm-tarani"></span>If you look at the trip origins for micromobility companies, customers aren’t looking in the app ahead of time. People see a vehicle and then they use it.</li> </ul> <p>What does the future of the vehicle look like? There’s quite a few changes that have happened since the gen 1 scooters, and now gen 3 is deployed and we’re talking about gen 4 and its cognitive abilities. <strong>What does adding onboard intelligence imply and what does that opportunity look like?</strong></p> <ul class="mm"> <li><span class="mm-assaf"></span>If you look at the industry today, there’s no gen 1, gen 2, or gen 3s. They are all gen 1. Scooters today are basically technology from the 70s, with their extruded aluminum and L shapes. Micromobility will have many different shapes depending on the distance and the task and also on the shape of the city. Mechanical improvements take you only so far.</li> <li><span class="mm-assaf"></span>For personal vehicles, you’re going to take care of it to a certain extent. If the brakes are not working, you’re going to take it into service. You’re going to ensure the battery is charged. Once you deploy vehicles in the streets, that equation is broken because you’ll need as many caretakers as riders. It doesn’t scale up. At the same time, you have a much higher utilization of the asset.</li> <li><span class="mm-assaf"></span> So the only way to take care of the problem is to automate inspection and have the service quality be a lot higher. So the vehicle needs to be able to ask itself before every ride: am I safe to ride? are the battery cells all connected? am I in the right temperature range? are the brakes working? If the vehicle can do that, and <a href="https://superpedestrian.com">our vehicles do that</a>, that’s when we’ll see higher safety levels and lower costs. Otherwise we won’t be able to make money on this and companies won’t be able to survive.</li> <li><span class="mm-tarani"></span> There’s a classic mistake in transportation which is over-capitalization and lack of investment in maintaining what we’ve built. We can see that in the $5 billion invested into micromobility to date. Sensors, telematics, and maintenance data that you can grab from the vehicle remotely is all interesting.</li> <li><span class="mm-tarani"></span> The push ahead of autonomy is about generating a return for a vehicle even with all of its wear parts. Those wear parts, such as brake pads or a bell or a light, are designed for ease of maintenance. And a big problem area is simply getting the earned revenue to be more than the cost to buy it and maintain it.</li> <li><span class="mm-tarani"></span> My concern with telematics is that if we are going to place vehicles in the field, we’re responsible for those vehicles. And we have a lot more R&amp;D to do in creating a safe vehicle that can last a long time and is easily maintainable by trained employees.</li> <li><span class="mm-assaf"></span> Telematics is too little too late. Knowing that a vehicle is broken somewhere in the field is not as good as preventative measures to protect itself in real-time. We’ve been able to deploy automation that prevents fires.</li> <li><span class="mm-assaf"></span> But vehicles fail more often than you realize. And if you’re next to a car and your vehicle is not stable, this is a major safety concern. And then cost follows, whereas when 20% of vehicles failing switches to 5%, it changes your model completely.</li> </ul> <p><img src="/media/photos/micromobility-mtf.jpg" width="500" /></p> <p>Thanks to our four panelists and to Horace Dediu and Chase Stubblefield for keynoting the conference. You can follow each of us on Twitter to stay up to date on micromobility news and opinion: <a href="https://twitter.com/TaraniDuncan">@TaraniDuncan</a>, <a href="https://twitter.com/andrewsalzberg">@andrewsalzberg</a>, <a href="https://twitter.com/superpedestrian">@superpedstrian</a>, <a href="https://twitter.com/chasestubb">@chasestubb</a>, <a href="https://twitter.com/asymco">@asymco</a>, and <a href="https://twitter.com/samuelclay">@samuelclay</a>.</p> <style> .mm li { position: relative; } @media (max-width: 48em) { .mm li { padding-right: 36px; } } .mm span { position: absolute; right: -36px; top: 0px; bottom: 4px; width: 18px; border-radius: 4px; } @media (max-width: 48em) { .mm span { right: 0; } } .mm-andrew { background-color: #d1e8d1; } .mm-tarani { background-color: #d1e5e8; } .mm-assaf { background-color: #dfd1e8; } .mm-matt { background-color: #e8d1d1; } .mm-sam { background-color: #e8e3d1; } </style>Samuel ClayI moderated a panel on micromobility at the annual Moving the Future Conference at Harvard Business School. I had the fortune of discussing this decade’s favorite topic with four heavyweights in the industry. We had the panel discussion on Groundhog Day, when Punxsutawney Phil saw no shadow and thereby predicted an early spring (ain’t that the truth), We discussed the potential for micromobility, the cannibalization of walking and transit usage, aggregation of micromobility vehicles, and what the future of the vehicle looks like. Here’s all the good stuff and memorable lines from the 40 minute conversation. The micromobility panel includes, in order from left to right: Matt Warfield, a new mobility planner with the City of Boston Transportation Department Tarani Duncan, a product strategist and advisor to pioneering autonomous micromobility company Tortoise and spatial analytics company Zoba Assaf Biderman, CEO and founder of Superpedestrian, a robotics company building the next wave of intelligent and connected micromobility vehicles Andrew Salzberg, former head of transportation policy and research at Uber and current Loeb fellow at the Graduate School of Design at Harvard Samuel Clay, moderator and graduate student in design engineeringThe New Garden of the World — Review of The Machine in the Garden2019-08-07T04:20:00+00:002019-08-07T04:20:00+00:00https://ofbrooklyn.com/2019/08/7/machine-in-the-garden-leo-marx<hr /> <p><i>The Machine in the Garden</i>, written in 1964 by Leo Marx, explores the relationship between the pastoral ideal and the industrial progress that ostensibly is in opposition to that ideal. This book is not necessarily a literature review, although it enlists a half dozen full-length writings to understand the cultural symbols that encode the interplay between imagination and reality.</p> <p><img src="/media/photos/machine-in-the-garden.jpg" style="width: 500px;" /></p> <p>The urge to idealize a simple, rural environment is strong throughout the two centuries of progress that mark the industrialization of the United States from the American Revolution through until the end of World War II.</p> <p>The literature that Marx introduces to illustrate the division between the two realms are masterworks that also serve as products of their times. <i>Moby Dick</i> by Herman Melville, <i>Walden</i> by Henry David Thoreau, <i>The Tempest</i> by William Shakespeare, <i>Adventures of Huckleberry Finn</i> by Mark Twain, and <i>Ethan Brand</i> by Nathaniel Hawthorne are instruments played in the concordant symphony of poetic realism that unites the two fronts.</p> <p>At no point does Marx introduce the actual machines of the machine age to refine his point, instead preferring to rely on mid-Nineteenth century literature and art to capture the sensory images of the age.</p> <p>Marx asserts that the authors of the 1800s addressed the alienation and environmental blight that accompanies that rapid industrialization that the building and deployment of the continental railroads brought with them.</p> <p><b>The central question that Marx attempts to address is how come there is an attitude of hostility towards civilization?</b> What is it about the products of industrialization, which bring us a miracle of goods and services that enrich our lives in innumerable ways, that provides us the impetus for abandoning them in order to reduce the technical complexity that has befallen us? What is attractive about the pastoralism that is represented by the untouched landscape, revered at Walden Pond, that is unspoiled and identified by nature as much as a rejection of the artificial world?</p> <p><img src="/media/photos/inness-the-lackawanna-valley-1855.jpg" style="width: 500px" /></p> <p>The romantic pastoral, a literary device embodied in the semi-primitivism of the cultivated landscape, is located chiefly by its opposition of the forces of civilization, the so-called fever of the world. It’s hard not to be taken by the opposing scene of the abstract when faced with the overwhelming force of a dehumanizing and polluting industry.</p> <!--more--> <hr /> <p>Ultimately, Marx believes that the problem presented belongs not to the artist but to politics. Thomas Jefferson, America’s third president and a departure of the friendly mercantilist policies of the Washington and Adams administration, developed his idea of a flourishing pastoral America in 1785 when he published Notes on Virginia.</p> <p>Jefferson’s views on the frontiers of science color his admonition of European values, calling them “destitute of national morality.” He had witnessed the machines and factories of Europe when he lived abroad and understood that they were a harbinger of industrialized cities and their environmental consequences.</p> <p>But Jefferson does not argue from a primitivist standpoint. When he writes of Native Americans and their lack of laws providing a happiness that stands in contrast to the over-regulated European condition, he is providing his affirmation of the pastoral in that he is stressing a range of possibilities unavailable to Europeans.</p> <p>In America, Jefferson writes, the primitive life is a drudgery and force is law. This is in contrast to the pastoral ideal, where his countrymen would be served better to be dependent on European manufactures instead of importing the misery that accompanies the industrialization of civilization. Here Jefferson argues that American is the exception to the rule that a country should be economically self-sufficient due to its exceptional abundance of land and its accompanying natural resources.</p> <p>Marx argues that Jefferson belies his pastoral vision in this moment as he is devoted to agriculture largely as a means of preserving rural virtue. In practice this meant shipping raw materials to European factories in exchange for finished cost, no matter the financial cost. But he admits that this can be economically disadvantageous and that the loss of material living standards should not be a test of a good society.</p> <p>This loss is counterbalanced by the “happiness and permanence of government” that accompanies the rejection of productivity. In a dour picture of the future, Jefferson ultimately warns that if America develops its own system of manufactures then the fresh, sunlit atmosphere of the countryside will be replaced by dark, foul air characteristic of European cities.</p> <p>Curbing the economic developments of manufacturing would require the kind of governmental power that Jefferson abhorred. This contradiction, seemingly founded in a fondness for the agrarian lifestyle, would be antithetical to how the pastoral ideal would flourish.</p> <p>This ambivalence is a response to the conflicting demands of industrial society and the young country. As Alexis de Tocqueville notes, the wilderness was precious not for its current state of peace and happiness but for what could be made of it.</p> <p>Tench Coxe, who was notable for serving under both Hamilton as the Assistant Secret of the Treasury and then under Jefferson as purveyor of public supplies, also wrote on the economic effects of manufacturing in his Report on Manufactures. Coxe had the foresight to see beyond Jefferson’s vision. Coxe believed that machines bring out the powers latent in the environment. For example, textile manufacturing more consistently fit in with the ideal of a natural America than of Europe.</p> <p>Hamilton and Coxe foresaw this potential in establishing America’s supremacy over other nations. Hamilton endorsed a program of embracing manufacturing in order to turn the United States into a behemoth akin to a corporate entity. Coxe, who supported Hamilton, preferred a subtler means of spreading this message by using the more farsighted approach of using language coached in the pastoral ideal and not to acknowledge the wealth and power that inevitably accompanies industrialization.</p> <p>Coxe writes that “unless business of this kind is carried on, certain great natural powers of the country will remain inactive and useless.” By building factories, America unlocks her potential and fulfills the narrative set forth by the founding of the country. The machine accelerates history and offers a destiny that preserves the naturalness of rural life in a way that is accessible to more people than the landscape naturally supports.</p> <hr /> <p><img src="/media/photos/sheeler-river-rouge.jpg" style="width: 500px" /></p> <p>The nation rejoiced at the introduction of the locomotive, even as it provided many writers and artists an outlet for the misgivings that they had for the intrusion of the machine. There is a pervasive theme in our literature that describes the green landscape that is incurred upon by the machine.</p> <p>Even the artworks that Marx selects, <i>The Lackawanna Valley</i> by George Inness and <i>American Landscape</i> by Charles Sheeler, portray an accelerated vision of the absorption of industry and progress into an American landscape.</p> <p>Inness’ painting, which was commissioned by the railroad it depicts and is used as the cover of the latest printing of this book, assimilates the machinery into the landscape and seamlessly blends the artificial construct with the hills, trees, and natural terrain.</p> <p>The smoke from the passing locomotive is figured with the same strokes as the puffs rising up from the church. Instead of cutting the painting into sections, the graceful curves of the train tracks delicately unify the pasture with the town. Hard edges are softened and the solitary figure is seen reclining in a relaxed position, at harmony with the scene unfolding below. Marx points out that even the animals continue to graze peacefully.</p> <p>It is this narrative of the two worlds meeting harmoniously that sets the stage for the acceleration in the 20th century. The organic qualities of Inness’ painting are contrasted with the regimented lines and overwhelming abstract figures of Sheeler’s industrial landscape of Ford’s early 20th century River Rouge plant, also commissioned by the company.</p> <p>There is no trace of untouched nature in this landscape. In this landscape, technological progress overwhelms the solitary figure, using the human scale to provide for an industrial scale. Marx argues that this bleak vista is softer than the reality, with the whirring clamor of the machines obscured and the surreal staging of the industrial process providing a pastoral sublimity that corroborates the scene in Inness’ work.</p> <p>But do these two works show the dominant narrative that is embraced by Americans? It would serve Marx’s work well to include examples of a more Jeffersonian rejection of industrialization, if only to have it serve as a catalyst for the inevitable reaction.</p> <hr /> <p>Even Emerson, mentor to Thoreau and a critic of the pressures of society, welcomed the new factory system. He wrote in his journal in 1837 that the logic was clear.</p> <p>Hard-pressed Yankee farmers no longer felt obliged to farm as their region provided a means of economic sustainability in manufacturing, a macroeconomic concept known as comparative advantage. The technology of the day was an auspicious sign that would advance literature as much as it would advance science.</p> <p>Emerson is no industrial apologist. But he sees the paradox of industry in that cities drain the flower of youth and that to support “whatever events shall go to disgust men with cities and infuse into them the passion for country life and country pleasures” is to support machine power as the conditions of science and technology can be made to serve a rural ideal.</p> <p>Emerson sees this machine power as a morally neutral force, which in and of itself can be potentially dangerous. The citizens of the town Emerson inhabits merely cope with the technology ceaselessly entering their lives.</p> <p>There is a famous G. K. Chesterton quote that says “progress should mean that we are always changing the world to fit the vision, instead we are always changing the vision.” Emerson sees his fellow citizens as accommodating their lives to the standard design of houses instead of designing their houses to best fulfill the purpose of their lives.</p> <hr /> <p>Overall Marx’s book provides a contemplative and throughly researched thesis, even as it sinews the pastoral mythology with the intransigent progress inherent to industrial society.</p> <p>However, in this book there are multiple examples of literature that do not serve to advance the theories put forth by Marx but rather feel as if they are an inconsistent chorus that mirrors the uncertainty felt by the people of the age. The yearnings for a bucolic paradise are felt in the actions of the characters in the novels that Marx presents, but, for instance, Ishmael witnessing the belly of a whale as a factory system turns an uncomfortable passage into a frustrating analogy.</p> <p>In the end, this book performs a convincing job of embracing the paradox between them two competing ideals, deepening my understanding of the parallels. Could there have been better analogies to mine from 19th century literature? Even with the choices made, Marx forges a timeless trail that is evocative of the pastoral ideal he blends with the ensuing mechanization of the 19th century.</p>Samuel ClayThe Machine in the Garden, written in 1964 by Leo Marx, explores the relationship between the pastoral ideal and the industrial progress that ostensibly is in opposition to that ideal. This book is not necessarily a literature review, although it enlists a half dozen full-length writings to understand the cultural symbols that encode the interplay between imagination and reality. The urge to idealize a simple, rural environment is strong throughout the two centuries of progress that mark the industrialization of the United States from the American Revolution through until the end of World War II. The literature that Marx introduces to illustrate the division between the two realms are masterworks that also serve as products of their times. Moby Dick by Herman Melville, Walden by Henry David Thoreau, The Tempest by William Shakespeare, Adventures of Huckleberry Finn by Mark Twain, and Ethan Brand by Nathaniel Hawthorne are instruments played in the concordant symphony of poetic realism that unites the two fronts. At no point does Marx introduce the actual machines of the machine age to refine his point, instead preferring to rely on mid-Nineteenth century literature and art to capture the sensory images of the age. Marx asserts that the authors of the 1800s addressed the alienation and environmental blight that accompanies that rapid industrialization that the building and deployment of the continental railroads brought with them. The central question that Marx attempts to address is how come there is an attitude of hostility towards civilization? What is it about the products of industrialization, which bring us a miracle of goods and services that enrich our lives in innumerable ways, that provides us the impetus for abandoning them in order to reduce the technical complexity that has befallen us? What is attractive about the pastoralism that is represented by the untouched landscape, revered at Walden Pond, that is unspoiled and identified by nature as much as a rejection of the artificial world? The romantic pastoral, a literary device embodied in the semi-primitivism of the cultivated landscape, is located chiefly by its opposition of the forces of civilization, the so-called fever of the world. It’s hard not to be taken by the opposing scene of the abstract when faced with the overwhelming force of a dehumanizing and polluting industry.Everything you need to build your own Turn Touch smart remote2019-01-03T04:20:00+00:002019-01-03T04:20:00+00:00https://ofbrooklyn.com/2019/01/3/building-turntouch<hr /> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*1_w7IlHISYWdQjIGPcxRkQ.jpeg" alt="" /></p> <p>Back in 2014 I was driving up the 101 coming back from a YC alumni Demo Day and I had a lightbulb moment about the way I wanted to control my new Hue lightbulbs. That’s when the idea of <a href="https://turntouch.com/">Turn Touch</a> was born. I’d had some experience building open source hardware <a href="http://www.ofbrooklyn.com/2014/01/2/building-photo-frame-raspberry-pi-motion-detector/">projects for my home</a>, and developing <a href="http://www.ofbrooklyn.com/2014/09/6/building-pulse-bloom-biofeedback-burning-man-2014">open source art installations for Burning Man</a> at a slightly larger scale, but I had never thought I’d build an open source hardware project that would be commercially available (or at least <a href="https://www.kickstarter.com/projects/samuelclay/turn-touch-beautiful-control?token=1ef790b6">available on Kickstarter</a>). <a href="https://www.turntouch.com">Turn Touch is now available to buy for $49</a> if you don’t feel like building your own.</p> <hr /> <p>There are four steps to building your very own Turn Touch:</p> <p><a href="/2019/01/3/building-turntouch/#firmware"><strong>Step one:</strong> Laying out the buttons and writing the firmware</a></p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*K_ERgjIZiFIX5yDOa5JJ7g.png" alt="" /></p> <p><a href="/2019/01/3/building-turntouch/#cad"><strong>Step two:</strong> Designing the remote to have perfect button clicks</a></p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*iXMqpq5IsfudAeT3GZlLFA.png" alt="" /></p> <p><a href="/2019/01/3/building-turntouch/#cnc"><strong>Step three:</strong> CNC machining and fixturing to accurately cut wood</a></p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*SYmywFE5tY3GBO9t9lcTiA.png" alt="" /></p> <p><a href="/2019/01/3/building-turntouch/#laser"><strong>Step four:</strong> Inlaying the mother of pearl</a></p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*kl8WDuTd0RpCkkipLeHx0g.png" alt="" /></p> <!--more--> <hr /> <p>Like many of you, I was one of those kids who took apart the stereos in my parent’s house to see how they worked. Since then I’ve been a full-stack engineer on most of my projects. Although I wasn’t quite sure how to build Turn Touch, I was committed to doing it, and learning how to build it from the ground up. At the Bay Area Maker Faire that year I stopped by the <a href="http://www.techshop.ws/">TechShop</a> booth and signed up for one of their new member deals.</p> <p>I build <a href="https://github.com/samuelclay">all my projects</a> Open Source, so when I decided to start building Turn Touch I knew I wanted to document my process. My plan from the beginning was to release 100% of the knowledge, planning, and design behind Turn Touch as an open source project. Through building Turn Touch, I’ve learned what it takes to create not just one remote, but an entire manufacturing process. So that’s what I want to share with you in this series.</p> <p>This is the full guide on how to make your own Turn Touch from scratch. This is the story of the design challenges faced when trying to make a seamless remote and how to overcome them. If you follow this guide, using the accompanying open source design files, then you will be able to build your own Turn Touch that you can use to control your smart devices and apps on your phone and computer.</p> <p>Through open-source hardware like Turn Touch, I’m working to lower the barrier to entry when it comes to creating and manufacturing your own complex hardware devices. Sure, it’s not what you might call a “traditional business plan”. But I strongly believe that by helping other people use the same tools I use, our community of makers gets larger and more inventive.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*3juMFkDjrO9yAKmO5Ay4wg.gif" alt="" /></p> <p>At the end of the day, this isn’t a project devoted to turning a profit. It’s a project devoted to makers, wherever and whoever they are.</p> <p>Pictured above and elsewhere in this series are all handmade prototypes. Turn Touch uses off-the-shelf components where it can, and only tools that are available to individual makers. No special tooling was required to build this remote. If you follow this blog series and use the included open source designs, you too can build a remote just like this.</p> <p>I’ve broken this manual up into four sections. If you’ve done a bunch of machining my lessons may seem elementary, but the hardware part may be useful. If you’ve designed a bunch of hardware but have never worked with wood, hopefully this will lower your barrier to entry.</p> <hr /> <p><a name="firmware" id="firmware"></a></p> <h2 id="step-1-design-challenges-with-bluetooth-for-a-woodenremote">Step 1: Design challenges with Bluetooth for a wooden remote</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*K_ERgjIZiFIX5yDOa5JJ7g.png" alt="" /></p> <p>At the heart of Turn Touch lies a circuit board with four tactile switches and a bluetooth module. This board alone is the remote, since it can be used without a wooden enclosure.</p> <p>The source code for the Bluetooth firmware, circuit board schematic, and board layout is <a href="https://github.com/samuelclay/turntouch-remote/">available on Github</a>.</p> <p>The circuit board is composed of four parts:</p> <ul> <li>Bluetooth module</li> <li>Other board components</li> <li>The PCB</li> <li>Metal dome sticker array</li> </ul> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*65BkQmv6MTMZ3EYkQaNnGg.jpeg" alt="" />The Turn Touch circuit board</p> <h4 id="bluetooth-module">Bluetooth Module</h4> <p>Choosing a Bluetooth radio isn’t trivial. In fact, choosing Bluetooth in the first place is a tradeoff.</p> <p>The goal for the remote is to control smart devices, but those devices range in terms of which wireless protocol they speak. For the most part, you can assume WiFi is in the equation.</p> <p>Philips Hue lightbulbs speak Zigbee, a low-power peer-to-peer mesh network, and connect to a hub that speaks both Zigbee and WiFi. Sonos speakers, Belkin Wemo smart power switches, Lockitron smart locks, and many others are also on WiFi.</p> <p><strong>So how about just using a WiFi module?</strong></p> <p>Part of the requirement for the remote is that a button press is near-instantaneous (with less than a 100 ms delay). But a WiFi module, such as <a href="https://learn.adafruit.com/adafruit-huzzah-esp8266-breakout/overview">the popular ESP8266</a>, draws up to 250 mA at 3V (750 mW) when transmitting and as low as 0.9 mA when sleeping.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*NCt78B1JDelqRlgFyCA1hg.jpeg" alt="" />Espressif ESP8266 WiFi module, <a href="https://www.adafruit.com/product/2491">$6.95 on Adafruit</a></p> <p>Considering a standard CR2032 coin cell battery has 225 mAh, you can run a WiFi module with normal use (sleeping 99% of the time) for only 28 hours if you want instantaneous feedback. You can use bigger batteries, but now you’re working on integrating giant AA batteries into a tiny hand-held remote, which won’t work.</p> <p>Better yet would be to bring the WiFi module down to the 10 µA level. But that requires a deep sleep which severs the connection. So when you press a button that requires the module to wakeup from deep sleep, it can takes several seconds to register the click. Amazon Dash buttons work this way and are able to last for over a year, since a several second latency is not a problem for them.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*k_7Pls_8SqkIAeKUBZC0dA.jpeg" alt="" /><a href="http://www.raytac.com/product_detail.php?id=38">Raytec MDBT40</a> Bluetooth LE module, $7.50 on Seeed</p> <p><strong>Your phone to the rescue</strong></p> <p>The good news is that while WiFi is too power hungry for this type of device, our phones and tablets, which are on WiFi and get charged every single day, work as a terrific relay. Our phones speak Bluetooth, so why not use the still fresh Bluetooth Low Energy (BTLE) protocol?</p> <p>There are a number of usable Bluetooth 4.0 modules to choose from. There’s the <a href="https://www.silabs.com/products/wireless/bluetooth/bluetooth-smart-modules/Pages/ble112-bluetooth-smart-module.aspx">BlueGiga BLE112 module</a>, <a href="https://www.sparkfun.com/products/12574">Roving Networks RN-42 module</a>, <a href="http://www.blueradios.com/nBlue%20BR-LE4.0-S2%20Summary%20Datasheet.pdf">BlueRadios BR-LE4.0-S2A module</a>, and the famous Nordic Semiconductor nRF51822 based MBDT40 by Raytec (pictured above).</p> <p>The principle concerns for a Bluetooth module are:</p> <ul> <li>The microcontroller and accompanying example firmware</li> <li>Ease of over-the-air device firmware updates (OTA DFU)</li> <li>Module size and profile</li> <li>Power requirements</li> <li>Debugging abilities</li> <li>Community and support</li> <li>Development kit and documentation</li> </ul> <p>Here’s how the most well known and easily available Bluetooth 4.0 modules compare.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*zWCXHHammpvbPA9k4yl8ZA.png" alt="" /></p> <p>While all of the modules fit the necessarily small footprint, the MDBT40 module with Nordic’s nRF51 series won out due to the robust community and plentiful open source examples. It also had twice the max output power with half the power requirements of the BLE112. Plus it came with example projects for over-the-air device firmware updates (OTA DFU), which allows the remote to be easily upgraded for firmware changes down the road.</p> <p>Not enough can be said about a thriving open source community around the nRF51 chip. Running into problems with power management, over-the-air device firmware updates, and even paramters for load capacitors on external crystals can be cleared up by consulting the massive online community that formed around the Nordic chip.</p> <p>Now that a Bluetooth module has been chosen, let’s move on to the passive electronic components that it needs to operate.</p> <h4 id="other-board-components">Other board components</h4> <p>The <a href="https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF51822">nRF51822 datasheet</a> is solid. The standard GATT attributes of the Bluetooth stack is easy enough to implement. What we’re looking for in the datasheet are low power modes. The remote will be asleep for most of its life, but it needs the ability to wake up and transmit the button press in under 100 ms in order to be called instantaneous.</p> <p>There are a couple options:</p> <ul> <li>Maintain connection with the phone/computer but in a low-power sleep. A coin cell battery lasts 1 year in this mode.</li> <li>Disconnect and power down to a lower-power deep sleep. A coin cell battery can last 3–5 years in this mode.</li> </ul> <p>I chose the 1 year option. Interestingly, if I had chosen the 3–5 year option, the quickest way of waking up and transmitting a button press to a phone uses a special advertisement packet that can take up to 4 seconds to send and be read. These numbers are adjustable and there are certainly graphs you could draw, but I found the tradeoff to be worthwhile by staying connected and being limited to 12 months of battery life.</p> <h4 id="considerations-for-lowpower"><strong>Considerations for low power</strong></h4> <p>From the datasheet we see that the low power mode works best with a 32.768 kHz external crystal oscillator with 9 pF load capacitance and a ±20 ppm frequency tolerance. <a href="https://devzone.nordicsemi.com/question/5186/how-to-minimize-current-consumption-for-ble-application-on-nrf51822/">This Nordic Semiconductor support thread on minimizing current consumption</a> has the details, and <a href="https://devzone.nordicsemi.com/question/953/what-low-frequency-clock-sources-can-i-use/">this thread can help you determine the crystal specs</a>.</p> <h4 id="considerations-forbuttons"><strong>Considerations for buttons</strong></h4> <p>Apart from the external crystal used for low power, the only other peripheral we have on the microcontroller are the four buttons. These are internal pull-up GPIO pins with interrupts. This means that when the button is pressed in, the module is running at full power. One optimization to make would be to go back to sleep, but that would require external pull-up resistors, 1 per button, for a total of 4 more resistors on the board.</p> <h4 id="the-pcb">The PCB</h4> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*IsayLJdUYwWGvWpHBCoEXQ.png" alt="" />The Turn Touch circuit schematic</p> <p>Follow the rules and you get the schematic drawing above. I used EAGLE. Next time I would use KiCAD, if only because with open source EDA software it becomes easy to share the schematic and board layout with consultants and online forums for debugging and optimization.</p> <p>You can <a href="https://github.com/samuelclay/turntouch-remote/tree/master/remote%20eagle">download the board layout on Github</a>.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*eVM-tGHwvdzFM5jSw-JUDw.png" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*S1fzU2aXV_PYRxSvP-Ydcw.png" alt="" />Top (left) and bottom (right) layout of the circuit board</p> <p>Here’s where we get our first intersectional consideration. Before we were just thinking about what happens to the performance of the remote based on decisions made about the firmware and microcontroller. But the shape of the circuit board needs to know what the rest of the remote looks like.</p> <p>The way I attacked this problem was to declare the minimum size for the circuit board and battery. A geometric symmetry is desired, but for the battery holder and the bluetooth module to fit, they had to be rotated 45°. From that requirement, tracks were routed around the five mounting holes. Two of the mounting holes are nudged a bit apart (the two in the top right) so that the board cannot be inserted upside down or in any other orientation on the same mounting pins.</p> <p>The 8 magnets that hold the remote together look like they have bitten the PCB on all four sides. The reason for those divets is that the buttons themselves only take up so much space, considering their size is dictated not by electronics but by design. But the PCB is bigger than the button size as a matter of fitting both the battery holder and the bluetooth module. So we can cut into the board a bit with the magnets and just route the tracks around them.</p> <h4 id="metal-dome-tactile-button-stickerarray">Metal dome tactile button sticker array</h4> <p>Buttons come in many shapes and sizes to satisfy different requirements. I spent a lot of time looking at buttons that were industrial sized and could take pinball machine levels of abuse. Finding a way to press down and make contact was not trivial. Getting to “metal dome array” took a long, long time.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*p9bN3Zy2vhkh5KVvRr5dPw.jpeg" alt="" />Metal dome sticker arrays are custom yet inexpensive</p> <p>The requirement for the remote was to have a low profile (short) button, but all of the mechanics of off the shelf buttons have a vertical profile of nearly a centimeter.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*fVU31Of0SobGLR9PGe88Yw.jpeg" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*K8NXm_QjhbVeyAFlrhahjQ.jpeg" alt="" />E-Switch <a href="http://www.digikey.com/product-detail/en/e-switch/TL3315NF250Q/EG4622CT-ND/1870402">TL3315</a> Series (L) and Panasonic <a href="http://www.digikey.com/products/en?FV=ffec59b6">EVQ6P6</a> Series (R)</p> <p>You can find surface mount tactile switches for $0.20 on Digikey. The E-Switch is one example. There are two reasons I did not choose to use this, one pragmatic and one aesthetic.</p> <p>Practically speaking, if you have surface mount components on two sides of a board, you just doubled your PCBA (PCB assembly) costs, since the board house has to solder one side at a time, holding one of the sides down in order to solder the other side. It’s time consuming and costly, and if you can get away with a single-sided board, you should.</p> <p>Aesthetically speaking, there’s this phenomenom I like to call “accidental swastika”.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*LMaJwKmXFEnPCPCQ0jMW_g.png" alt="" />Whoops</p> <p>By using a sticker dome array, we save money on PCBA but instead have to adhere the dome array during the full box build assembly process later on. Aligning a sticker array takes far less time, and we have the ability to choose between different click feels, pressures, and click sounds with tactile metal domes.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*DpY0Bt0wQ808XOcmFVhC1g.jpeg" alt="" />Depth of field for a half-inch metal dome</p> <p><a href="http://www.snaptron.com/products/standard-domes/m-series/">Snaptron M-Series metal domes</a> were the first dome I tried. It was magical. All I had to do was design the board layout with two concentric rings, add a via in the center as an escape for air, and tape the metal dome down.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*a4zK8LbjcYBumsSo-obrOw.jpeg" alt="" />Taping metal domes with scotch tape on the left, custom made button array on the right<img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*ar6NE8SLL6xaD89OfCMaUw.gif" alt="" /></p> <p>Ordering a metal dome array was easy. I just had to upload my gerber CAM files from EAGLE and send it off to <a href="http://www.china-metaldome.com">CMD</a>. $100 later I had 100 metal dome arrays with 400 individual buttons. Peeling them off and sticking them in the right spot is quick and accurate.</p> <p>We now have a functional circuit board with a long-lived battery running Bluetooth. It’s now time to design the remote enclosure.</p> <hr /> <p><a name="cad" id="cad"></a></p> <h2 id="step-2-learning-to-build-cad-models-of-a-woodenremote">Step 2: Learning to build CAD models of a wooden remote</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*iXMqpq5IsfudAeT3GZlLFA.png" alt="" /></p> <p>The source code for the CAD models is <a href="https://github.com/samuelclay/turntouch-enclosure/">available on Github</a>.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*hSHOYK-KBSDjhD_tA1P0Fg.png" alt="" /></p> <p>Now let’s put it all together. The remote is broken up into 7 stacked parts, starting at the top:</p> <ul> <li>top four wood buttons (beige)</li> <li>top wood case (gold)</li> <li>top plastic insert (light grey)</li> <li>plastic button arms (teal)</li> <li>circuit board (purple)</li> <li>bottom plastic insert (dark grey)</li> <li>bottom wood case (gold)</li> </ul> <p>Starting the CAD process wasn’t easy. I didn’t even know which CAD program to use. I tried four programs, in order: Sketchup, Rhino, Solidworks, and finally Autodesk Inventor.</p> <p>The <a href="https://github.com/samuelclay/turntouch-enclosure/tree/master/archive/sketchup">models for Sketchup</a> were limited by the simplistic capabilities of Sketchup, but Sketchup to its credit does a wonderful job of introducing you to the basics of CAD.</p> <p>The <a href="https://github.com/samuelclay/turntouch-enclosure/tree/master/archive/rhino">models for Rhino</a> were limited by the non-parametric capabilities of Rhino, where it was easy to model shapes but extremely difficult and time-consuming to change the parameters that determined how models affected each other.</p> <p>The <a href="https://github.com/samuelclay/turntouch-enclosure/tree/master/archive/solidworks">models for Solidworks</a> were just the right balance of ease of creation and future modifiability. But once I tried Autodesk Inventor, its admittedly minor differences from Solidworks won me over. And while Autodesk’s Fusion 360 wasn’t production ready when I started the process, if I were to begin again today I was absolutely use it.</p> <h4 id="figuring-out-how-pieces-fittogether">Figuring out how pieces fit together</h4> <p>The question to consider when designing each of these layers is what is their relation to the layer immediately above and below itself. The circuit board defines where its mounting pegs are for aligning with the plastic insert below it, and it defines the button dome positions on top for aligning with the plastic button arms above.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*qY_YB1u6m3e58ASrYJea2Q.png" alt="" />Seven layers: wood buttons, top wood case, top plastic insert, plastic button arms, circuit board, bottom plastic insert, bottom wood case</p> <p>There’s quite a bit of overlap, so each of these pieces fit together nicely to form a much more compact package.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*HSHKaV2v1k2jjRwWjUyzeA.png" alt="" />2” wide × 2” deep × 0.68” tall (52mm × 52mm × 18mm)</p> <p>The circuit board defines the most fundamental constraints, so the design must form around it. That’s not always the case, but when the device is trying to be as compact as possible, it’s a good place to start.</p> <p>On the other hand we have the outer design, which is shaped to fit your hand. Those contours, while independent of the circuit board, are constrained to fit the circuit board footprint. So from the top constraints, inside and outside, we can begin dissecting the component layers and figuring out how to build this remote.</p> <h4 id="first-problem-how-to-hold-the-buttons-inplace">First problem: How to hold the buttons in place</h4> <p>The button travels down the height of the metal dome when pressed, so there needs to be some way of holding the button in place while it’s pressed down so that it reliably returns to the original position.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*aEhC7LYqhteZeAc4bKeJsg.png" alt="" />The actuator is tall enough to allow the button arms to bend without deformation</p> <p>Below you can see the concept of buttons arms. Each button has two arms that angle out diagonally from the center of the button. This holds the button in place from the sides.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*Vglx003GWuRPD0rBRpzCeQ.png" alt="" />The button array on the left attaches to the top enclosure on the right, held in place by its own structure</p> <p>The remote has no border between buttons, so the button arms cannot cross over on top of other buttons. Two arms come from opposite corners and merge into the button half-way down its length.</p> <p>Why not extend the arms all the way down to the center of the remote? Extending the arm would cut the deformation in half, since each part of the arm has half the height to travel when pressed. But it would also compromise the strength of the button, allowing it to break during assembly.</p> <p>The length of the arms is a good compromise between strength and flexibility.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*L-Nl9XbdiIOvDwRT_o1XZg.jpeg" alt="" /></p> <p>Possibly my favorite design inside the remote is how the buttons are held in place. Originally there were pegs in the insert and holes in the button arms. This worked but not well. It was at the mercy of various tolerances when building two separate plastic pieces: the top insert and the button arm. Any misalignment had to be corrected for on the other piece.</p> <p>If the peg was too fat, the button arm’s hole had to be widened using a miniature, conic jeweler’s file. If the peg was too narrow, the button arm had to be adhered somehow to the peg to prevent it from slipping off. It was a nightmare.</p> <p>By using tabs that reach over the top insert, the buttons hold themselves in place. During assembly the button array needs to be bent into place, but once attached it doesn’t go anywhere. This also solved the issue of broken pegs when buttons are forcefully pushed in during assembly.</p> <h4 id="second-problem-pressing-on-any-part-of-the-buttonface">Second problem: Pressing on any part of the button face</h4> <p>Users are going to press a button on any part of the button. And if the press isn’t directly in the center, then it needs to be accommodated.</p> <p>Each button is held in opposite diagonal corners, so when a button is pressed the arms deflect slightly downward. But they also form an axis that the button can rotate on.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*Mlt1f9YDNXuLflsbRj1xNg.png" alt="" /></p> <p>If the button is pressed anywhere not on that axis of rotation, it will either push up the inside corner or outside corner and pull down the other side. To prevent this, we add a paddle on the outer corner of the button. This paddle is aligned with the plastic inserts so that it has a tiny bit of wiggle room but ultimately is constrained from moving up or down, preventing the button from rotating.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*H-C4GP1FNMwUwnSoS-k3ew.png" alt="" />The blue paddle rests between the bottom and top plastic inserts, preventing the button from rotating when pressed</p> <h4 id="third-problem-holding-the-remotetogether">Third problem: Holding the remote together</h4> <p>During a demo this is everybody’s favorite part. The top and bottom of the remote need to be held together so that the end user can open up the remote and swap out the battery.</p> <p>Most remotes use a plastic latch that either needs to be pinched by the user or pulled apart. Alternatively, some remotes are round and can be rotated around internal threads to open.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*CCPkSIXazJl1VVLaAqgj_Q.png" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*3jeUDHoiA2xo4kJQj-g_9Q.jpeg" alt="" /></p> <p>To solve this problem, I turned to a set of eight strong neodymium disc magnets. They get glued and placed into holes that are sized slightly smaller than the disc magnet, forcing the opening on the side of the hole to expand slightly. This holds the magnet in place and, coupled with an adhesive, ensures a strong hold on the magnet.</p> <p>It is so satisfying to pull a remote apart and let it snap back together with the power of mangetism. And these magnets are strong, so they pack a lot more punch than anything else this size.</p> <h4 id="fourth-problem-accounting-for-alignment-tolerance-stackup">Fourth problem: Accounting for alignment tolerance stackup</h4> <p>This problem has been one of the most perplexing and unforgiving issues I’ve come across on the remote. The issue is that the 7 separate pieces of the remote need to be adhered together in a way that compensates for the different relative tolerances and variable sizes of each component.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*8LIlphMSCmhFPMZHy-yQ9w.png" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*75hSqvyIhqAePK6KzW4gYQ.png" alt="" /></p> <p>There are only a few sizes that must remain fixed. These are the circuit board, which has been fixed in size due to the requirements of the button positions. Second are the wood case’s top and bottom pieces need to be the same size and aligned perfectly with each other so that they form a seamless lip.</p> <p>The trick is to have a buffer that can absorb the differences in tolerance between the two constraints. This buffer is between the inside of the wood and outside of the plastic insert. That space is apportioned to allow for inaccuracies that arise from two-sided machining.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*6vDX8XxzezKPYAGkqJqLKg.png" alt="" /></p> <p>What the above image shows is that the wood, on top, has a variable amount of space for the plastic insert to move around and align itself. The problem that arises during machining is that the top and bottom may not be evenly aligned, so the tolerance has to account for the maximum offset on one top and the maximum offset in the opposite direction on bottom.</p> <p>While there is no issue with having a buffer that moves the button arms around, the buttons are no longer perfectly aligned with the opening in the top of the case. To account for this, the plastic button arms are inset from the edges, as evidenced by the purple circuit board peeking around the margins of the teal button arms below.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*RLsILQKmCX3lDeBOxrz88Q.png" alt="" />Smaller button arms (teal) allow the wood to be independently aligned without overlapping other buttons</p> <p>The wood buttons are glued on top of the plastic button arms, so they don’t need to be perfectly aligned. But the plastic arms are reduced in size so that a wood button isn’t sitting on top of two plastic arms that happen to be slightly offset due to any alignment issues.</p> <p>But this brings up another issue. We want to maximize the amount of wood at every point while minimizing the total size. We do this because otherwise the wood could break in the thin sections during machining.</p> <p>Let’s turn back to the see-through side view of the remote.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*bT6pKo2uxOw_Glp3hx6JBQ.png" alt="" /></p> <p>Notice the space by the yellow arrow is a weak spot that runs along the circumference of the remote. By minimizing the height of the plastic insert we can maximize the wood that holds it in. This minimizes the possibility of split wood during the machining process.</p> <p>And with that we come to the most exciting part of the process, how to successfully machine the wood.</p> <hr /> <p><a name="cnc" id="cnc"></a></p> <h2 id="step-3-cnc-machining-and-fixturing-to-accurately-cut-a-woodenremote">Step 3: CNC machining and fixturing to accurately cut a wooden remote</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*SYmywFE5tY3GBO9t9lcTiA.png" alt="" /></p> <p>Now that we have our models, we need to figure out how to machine them out of wood. Workholding is the principle concern here. In order to consistently machine the wood on two sides, the wood needs to be held down. This is much harder than it sounds.</p> <p>The source code for the CAM toolpaths explored below is <a href="https://github.com/samuelclay/turntouch-enclosure/">available on Github</a>.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*41Vskcx9fJWeav32Fx6IZQ.jpeg" alt="" />Lumber and machining fixtures line the entire wall in my office in SF</p> <p>There are two central issues to consider: one is that each piece needs to be machined on both sides, and second is that the workpiece needs to be held down both facing up and facing down so that it can be machined on both sides. In our case, we want to scoop out the insides of the remote out to fit the circuit board and button arms while contour cutting the outsides of the remote to fit the desired shape.</p> <p>Designing the CNC machine toolpaths to sculpt the contour of the case while pocketing out its insides, assuming the workpiece is held down, is not terribly difficult. There are only a few strategies that you need to use and they mostly involve answering the question of how fast to cut while maintaining as clean a cut as possible, which we’ll get to later.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*FCAE9gzG-YWB-ZKkwlAsTA.png" alt="" /></p> <p>Above, I’ve designed the fixture that gives us 15 pattern-matched remotes at a time from a single board of wood 24” long and 7” deep. That’s just about 1 board-foot (bf) of wood. A single board-foot costs anywhere from $5 to $50, with maple at $5/bf, mahogany at $10/bf, padauk at $15/bf, and rosewood at $40/bf.</p> <h4 id="pattern-matching-thewood">Pattern matching the wood</h4> <p>The top half of the fixture is the inside of the remote, the bottom half is the outside of the remote. We’re machining both the top and the bottom next to each other at the same time because we want the wood to pattern match.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*b7Fcr5SoksZSR0asVIUk6g.png" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*vfFyWOKXOd_ZNC-tSleDYg.png" alt="" />Examples of pattern matching with mahogany on the left and padauk on the right<img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*SgNbKWiAqSiGV5wRJLIAsQ.png" alt="" />Pattern-matched Rosewood halves come together beautifully to form a single remote</p> <p>Since we want the top of the remote to match the bottom of the remote, they must be machined next to each other. Our fixture reflects this by placing a top half next to a bottom half, and always in the direction of the wood grain. That way when the top case is flipped over like a book and closes on the bottom case, the grain lines match up.</p> <h4 id="how-to-align-the-wood-for-two-sided-machining">How to align the wood for two sided machining</h4> <p>Let’s start with the first fixture design because it was simple and was designed to only make a single remote at a time.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*oWvxvtMGYpNRBve8QBzWAQ.gif" alt="" />The first fixture that successfully machined a remote</p> <p>This animated gif beautifully illustrates the basics of machining the remote on both sides. This design uses a vise clamp that will quickly prove to be haphazard and inaccurate.</p> <p><strong>Unfortunately, the truth is that using a vise clamp leads to needless suffering.</strong></p> <p>The hardest part about using a vise clamp is ensuring that the registration and orientation match when flipped upside down. Not only do points on the x, y, and z axes have to be the same, but they need to be the same all the way down the workpiece. Any accidental tilt, turn, or shift when flipping the wood blank over results in a mis-alignment, and when that shift is greater than the width of the wood, we’re left with a work piece that abruptly breaks during milling.</p> <p>And to make matters worse, errors are doubled when the piece is flipped. A 0.005” shift (less than the height of a fingernail) is a proportionate shift in the other direction on the opposite side, resulting in breakage happening more often than such a slight shift would imply.</p> <p>The good news is that it’s easy to design a better fixture. The vise clamp suffered from multiple issues. Having to hold down the vise clamp itself was problematic, as it could subtly shift during machining. There are constant orientation and registration issues with a vise clamp unless you are fortunate enough to have a CNC track that can securely hold down the clamp.</p> <p>We can clean these issues up with a fixture cut into a ¾” plywood board.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*tmAr_akkmAy9ZJ0wsS8Etw.jpeg" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*HsyxPF42hjpxeGyPIJXUdg.jpeg" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*N0ZynMYhzrGoSrDID1GPiA.jpeg" alt="" />Nearly finished with buttons on the left and cases on the right, but still needs to be cut out and sanded down before finishing.</p> <p>This is fundamentally the same fixture as the vise clamp with a few important differences. See those alignment pin holes in the top image? Those are going to be responsible for keeping our x and y axis consistent when the piece is flipped over. And the z axis is kept consistent with the use of a flat plywood board that is level to the CNC’s spoilboard.</p> <p>Note that the fixture itself is not registered to the table, it’s simply attached with screws. So when we put a blank wood work piece down, we drill the alignment holes directly into the wood and into the fixture. When one side is completed, we flip the wood blank over and use dowel rods to ensure we’re still registered in the same spot.</p> <p>So this fixture addresses alignment and orientation issues in all three axes, but because it is not itself registered to the table, we cannot reuse the registration holes, as the fixture is never going to be put into the exact same spot again next time it’s used.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*TULdMMKscIXSEZIH2Hea6g.png" alt="" /></p> <p>More importantly, there are still tabs holding the wood remotes in place during machining. Tabs take time to machine around since you can’t just have your toolpath wrap continuously around the contours of the remote without stopping. The CNC machine now needs to pick itself up and climb over the tabs for each rotation around the remote.</p> <p>Using tabs also means that the remotes cannot be placed right next to each other. There are these bars of unused and uncut wood between each half of the remote. These untouched bars allow the wood to be held down with clamps, since the remote isn’t sitting on anything. But they are a waste of wood!</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*AybBURwCSFn6mhOh5pChbQ.gif" alt="" />A belt sander once nearly ate through my fingernail from the top</p> <p>And to top it off, once the tabs are cut with diagonal cutters and you pull the individual remote halves out of the larger workpiece, you have to manually sand down the tabs, which is error prone and dangerous.</p> <h4 id="how-to-hold-down-the-wood-for-two-sided-machining">How to hold down the wood for two sided machining</h4> <p>Here’s where it gets interesting. In order to get rid of tabs, we have to figure out a way to hold down each individual remote as it is being machined.</p> <p>The cutter is contouring around the outside border of the case, so there’s no way to hold the case from above without having it in the path of the cutting tool. Ideally we’d hold it from below, but that requires messy adhesives or a complicated locking system. And that brings us to vacuums.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*f_Qnn9lCfHhmw3UMy_HWZA.jpeg" alt="" /></p> <p>Vacuum tables are outside our price range expensive, but we can build our own for the cost of plywood and glue. Above you can see that we’ve built a hollow box about 3” high. It’s just ¾” plywood cut into the shape of a box and glued tight. On top we’ve glued additional wood, which is then machined into the inverse of the remote.</p> <h4 id="for-a-fixture-alignment-is-everything">For a fixture, alignment is everything</h4> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*SMf9QntQrBweKSAHeOtigQ.png" alt="" /></p> <p>Zooming in we see that the top case (left) is too thin to use a large hole. So we drill a few dozen 1/8” holes around the perimeter of the top case.</p> <p>On the right we have the bottom case, which is solid in the center, so we can use a large 1/2” hole to have the vacuum hold it down in one spot.</p> <p>You may also notice 5 metal alignment pins on each half. The vacuum holds the case down in the z axis, but the alignment pins are needed to hold the case down in the x and y axis as the wood is being machined.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*MXeJkrRInOjdeP6Locqplg.jpeg" alt="" />A cutaway that shows the alignment pins do their job</p> <p>Above you can see how the 1/8” alignment pins handle the slight variance in the buffer between the case and the inverse fixture. They do a great job of holding the remote tight while it’s being worked over by the cutting tool. Before I used these alignment pins, oftentimes a case would become dislodged by the cutter, flinging it across the room.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*BwIVQc87zPX-cdx88Q0vyw.png" alt="" /></p> <p>The fixture itself sits on a spoilboard that is aligned to the machine, which allows for reuse of the fixture without having to recut anything. To pre-align the fixture, simply cut holes into your own spoilboard that sits on top of the machine’s spoilboard.</p> <p>Use those holes as reference guides for the dowel rod that aligns the fixture to the spoilboard. You can see the dowel rods sticking out of the fixture and into the pre-drilled alignment holes at the bottom.</p> <p>I’ve also cut out handles into the fixture, which makes carrying these heavy fixtures around much easier.</p> <h4 id="behold-thevacuum">Behold the vacuum</h4> <p>Just attach your vacuum hose to <a href="https://www.amazon.com/2-1-2-Flanged-Inlet-Fitting/dp/B003OCAY66">a $3 flanged inlet fitting</a> attached to the side of the fixture. You’ll need to cut a hole out of the fixture. I used a <a href="http://www.homedepot.com/p/Milwaukee-2-1-2-in-Hole-Dozer-Hole-Saw-with-Arbor-49-56-9669/202327740">$17 hole saw</a> on a drill press to poke a hole for the vacuum to attach to.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*7CYOzl81hnM1VdfKLzGwfw.jpeg" alt="" />The vacuum fixture uses a standard Shopvac 2¾” hose</p> <p>This is your standard 2½” hose, attached to an inexpensive ShopVac. You don’t need more than a couple horsepower to pull enough air to hold the wood down in place. This vacuum seal isn’t going to do anything for movement in the x and y direction, so if all it has to accomplish is offsetting the minor pull in the z direction due to vibration, then you won’t need an industrial pump or expensive vacuum to do the job.</p> <p>In other words, it shouldn’t hurt when you place your hand over the hose and seal the vacuum.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*LLt1oIIoc-d7kYuqemL0Iw.jpeg" alt="" />A beautiful rosewood workpiece being machined into five remotes</p> <p>Because the fit is so tight, we need to add tiny ejection pockets underneath the wood case so we can wedge a lever in and pop the remote out. You can see this space at the bottom in the above photo.</p> <p>You can see the secondary spoilboard on the button vacuum fixture, which follows the same principles as the case vacuum fixture. There are holes beneath each button, and three alignment pins holding the buttons in place as they are machined.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*lMXQQKBOLQUCKtKHNBelrQ.jpeg" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*uwLN8XQtgIsTkHwgDSynfA.gif" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*6iHBSBK1cznYu6Wf6Tm7TA.jpeg" alt="" />Maple (center) being machined, Brazilian Satinwood (right) cut up into eight remotes worth of buttons</p> <h4 id="using-off-the-shelf-tools-andcutters">Using off-the-shelf tools and cutters</h4> <p>It’s important to keep as many tools and parts of the assembly off-the-shelf. There’s something to be said about being able to order more components and tools from a trusted vendor rather than having to source new custom tools. All of the tools used to machine Turn Touch are relatively inexpensive and will last you long enough to machine thousands of remotes.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*McKRgd6pz38R4GVKUeQ9wA.jpeg" alt="" /></p> <p>The tools above are much of what I use in the wood shop. Starting with the milling and drilling bits, left to right:</p> <h4 id="18-shankbits">1/8” shank bits</h4> <ul> <li><strong>$3</strong>, <a href="https://www.mscdirect.com/product/details/05529128">1/8” brad point drill bit</a> — used on the vacuum fixture to provide a vacuum channel for the top case</li> <li><strong>$12</strong>, <a href="https://www.mscdirect.com/product/details/07764467">1/16” flat downcut 2 flute end mill</a> — used cut out the buttons without leaving a trace</li> <li><strong>$19</strong>, <a href="https://www.mscdirect.com/product/details/62754247">1/16” bull nose 0.01” radius 2 flute end mill</a> — used to sculpt the two north ridges on the buttons</li> </ul> <h4 id="14-shankbits">1/4” shank bits</h4> <ul> <li><strong>$36</strong>, <a href="https://www.mscdirect.com/product/details/03856192">1/8” flat 2 flute end mill</a> — this pokes small holes that alignment pegs fit into inside the top and bottom case</li> <li><strong>$33</strong>, <a href="https://www.mscdirect.com/product/details/76525062">1/4” flat downcut 2 flute end mill</a> — pocketing out the insides of the case without tearing out wood from the edges</li> <li><strong>$24</strong>, <a href="https://www.mscdirect.com/product/details/88248984">1/4” ball 2 flute end mill</a> — contouring the outsides of both the top and bottom case</li> <li><strong>$12</strong>, <a href="https://www.mscdirect.com/product/details/88247580">1/4” flat upcut 2 flute end mill</a> — surfacing and planing the wood for the buttons</li> </ul> <h4 id="12-shankbit">1/2” shank bit</h4> <ul> <li><strong>$17</strong>, <a href="https://www.mscdirect.com/product/details/01710334">1/2” flat 2 flute end mill</a> — used to dig tracks and layout for each of the fixtures</li> </ul> <h4 id="accessories">Accessories</h4> <ul> <li><strong>$1</strong> <a href="https://www.mscdirect.com/product/details/84980507">Square head power bit driver</a> — square heads don’t strip as easily as phillips screw head does</li> <li><strong>$30</strong> <a href="https://www.mscdirect.com/product/details/89099352">1/8” ER-25 collet</a> — size used by Shopbot CNC</li> <li><strong>$30</strong>, <a href="https://www.mscdirect.com/product/details/02307783">1/4” ER-25 collet</a></li> <li><strong>$30</strong>, <a href="https://www.mscdirect.com/product/details/01477082">1/2” ER-25 collet</a></li> <li><a href="https://raptornails.com/docs/sell-sheets/RAPTOR-B18-BRAD-NAIL.pdf">Composite plastic brad nails</a> — used to attach the remote fixture to the CNC’s spoilboard without damaging the spoilboard</li> <li><strong>$1</strong>, <a href="https://www.mscdirect.com/product/details/67366245">4 square head screws and washer</a> — long enough to go through the fixture and the spoilboard</li> <li>a pencil — don’t use a pen on wood, it smears and it kills the pen rather quickly</li> </ul> <h4 id="miscellaneous-lessonslearned">Miscellaneous lessons learned</h4> <p>Apart from the construction of the vacuum fixture, there’s many other lessons to learn from machining these remotes.</p> <p><strong>Figuring out the feeds and speeds for cutting into the wood</strong></p> <p>The feeds and speeds took a bit of trial-and-error but eventually I settled on values that meant an entire remote could be machined just under 10 minutes. With some changes to the cutting tools it is possible to bring that down further, but custom cutting tools cost on the order of $1,000. So the workholding fixtures we have here use off-the-shelf cutting tools, like flat end mills and ball end mills, which only cost $20 each.</p> <p>My rule of thumb for mahogany is 120 inches per minute with no more than a 0.15” step down. For rosewood that number is cut in a third, so 40 ipm. Finding that number is simply a matter of running the machine and interactively increasing or decreasing the feeds until there is no chatter and the CNC machine sounds like it’s under control.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*gkmprdnzAS0Xu8JbW2wQMA.png" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*pH17n6Z4HuGGYVwcC5oTqQ.png" alt="" /></p> <p><strong>How do I know if I’m moving too quickly?</strong></p> <p>Apart from the horrible sounds of chatter, this may happen.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*dJyEUt06ErHAclH7xHoVhA.jpeg" alt="" />I keep all of my broken cutters in a museum of mistakes</p> <p><strong>Different hardwoods need different cutting speeds</strong></p> <p>Some harder woods need to be machined at a slower speed than less dense woods. Rosewood needs its speeds cut into a third of what mahogany would use. Instead of having to update the toolpaths, I wrote <a href="https://github.com/samuelclay/turntouch-enclosure/blob/master/inventor/CAM/fix.py">this python script</a> to automatically copy the mahogany toolpaths and cut the speed in a third and then re-save them as rosewood toolpaths.</p> <p><strong>The inverse fixture needs to compensate for differences in the wood</strong></p> <p>Below is a picture of what the fixture looks like without the wood on top. Notice that the fixture is red at parts. That denotes that the wood is about 0.005” inset. This means that there is a slight gap between the inverse fixture and the wood workpiece that will fit over it.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*oRMMShDnuGNyGZ1orK2uSQ.png" alt="" />The internals of the front three remotes without the wood</p> <p>The reason for this slight gap is that if there is no gap then the workpiece will either not fit perfectly onto the fixture or it will get stuck, as it is holding the fixture on all faces. By adding a 0.005” gap between the two, they can better fit together. And the alignment pins and vacuum holes protect the wood from shifting around any axis, so the buffer could possibly even be bigger.</p> <p>With that, we now have perfectly aligned wood pieces that are ready to be adhered together. All we have left is to inlay mother of pearl on the bottom of the remote to complete the process.</p> <hr /> <p><a name="laser" id="laser"></a></p> <h2 id="step-4-design-challenges-with-laser-cutting-mother-of-pearl-and-laser-engraving-a-woodenremote">Step 4: Design challenges with laser cutting mother of pearl and laser engraving a wooden remote</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*kl8WDuTd0RpCkkipLeHx0g.png" alt="" /></p> <p>I set out to make the most beautiful remote I could. And after observing what many of the most beautiful handheld objects I’ve seen had in common, it became clear that Turn Touch deserves an inlay.</p> <p>The source code for the inlay paths and laser cutter settings is <a href="https://github.com/samuelclay/turntouch-enclosure/tree/master/inlay">available on Github</a>.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*q_ZAQm1B2AbKoFY8pNbGpg.jpeg" alt="" /></p> <p>The choice of mother of pearl came naturally. Wood and shell are both natural materials that are sustainably grown and can be beautiful to behold. The trouble with mother of pearl is that it is an exceptionally brittle material. Great care must be taken in both cutting the shell and then adhered it to the wood.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*Rvbar8w4OpFQ-V5smNypoA.jpeg" alt="" /></p> <p>There are two main considerations to make when thinking about cutting the mother of pearl to fit. First is that the remote needs to be engraved to perfectly fit the mother of pearl. Second is that the mother of pearl itself needs to be cut to fit the engraved wood with no margin between the two.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*MHL1zicAyCOZCATlK5CKZw.jpeg" alt="" />As thin as paper, this mother of pearl shell is only 0.007” thick.</p> <h4 id="engraving-thewood">Engraving the wood</h4> <p>Let’s begin with how I started the engraving process. The wood is considered the source of truth and will be engraved with no compensation for the mother of pearl. The shell must compensate for the wood.</p> <p>Originally I used the ShopBot CNC machine to engrave the logo into the wood using a 1/32” flat end mill. There were a few issues with using the CNC to engrave:</p> <ul> <li>Every inside corner turns into a 1/64” corner radius due to the limits of axial router cutters.</li> <li>The depth of the cut (height) kept changing due to machine tolerance.</li> <li>This involves a tool change, which is costly and time consuming.</li> </ul> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*LLgf8DykM610M_1PCE0j-w.png" alt="" /></p> <p>Worst of all, due to differences between machines, the laser cut mother of pearl would have slightly different dimensions from the CNC engraved wood in both the x and y axes, resulting in a not-quite-perfect fit, leaving <strong>a variable sized gap between the shell and the wood</strong>.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*ufHw40rEDXe4y36XRqyyAA.jpeg" alt="" />There’s an unfortunate margin between the engraved wood and the cut shell</p> <p>The fix is to use the same machine for both engraving and cutting so that the same machine tolerances are applied, resulting in a uniform offset between wood and shell.</p> <h4 id="using-the-laser-cutter-to-both-raster-cut-and-vectorcut">Using the laser cutter to both raster cut and vector cut</h4> <p>Engraving on a laser cutter is very different than engraving on a CNC machine. Whereas a CNC will route a tool bit in a pocket, stepping over itself with a minimal amount as to be efficient as possible, a laser cutter will rasterize the pocket shape and work in a way not unlike a 3D printer: line by line.</p> <p>Before we begin working with the laser cutter, we take simple safety steps to ensure our health. The first few times I worked on the laser cutter, after about an hour my throat started to hurt. Probably due to all the acrylic being massacred by other people sharing the pool of laser cutters.</p> <p>Wear gloves and wear a dust mask. Your lungs, skin, hair, and fingers will thank you.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*sE8hMAIyh7i7YFV2JScKlA.jpeg" alt="" />Nitrile gloves have no latex and are not coated in the messy dust that makes them easy to put on and remove</p> <p>Here’s what we’re going for. We need to accurately engrave into the wood. This means that the cut needs to be centered and oriented. If we were to just place the remote bottom side up and shove it in the corner of the work bed, the engraving would come out askew.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*DOQkCChXj2W-n-g5T6CvOA.gif" alt="" />60W CO2 Universal laser cutter, sped up 40X for dramatic effect</p> <p>Here we can see the laser cutter do its magic. To achieve this registration and orientation of the remote in the laser cutter, we need to do the same thing we did for the CNC machine: we make a fixture.</p> <h4 id="a-fixture-to-ensure-accurate-centering">A fixture to ensure accurate centering</h4> <p>Lucky for us, this fixture is easy to make. I just took a ¼” piece of plywood and ran the laser cutter around the outline of the remote at full power to cut through. I had to adjust my offsets a bit to fit the remote, but it only took a couple tries.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*_DTbjF5WhGKrymq8VYgy6w.jpeg" alt="" />This fixture gives us perfect registration and orientation for the laser cutter to accurately hit the center of the remote</p> <p>The remote is sitting inside this hole. Because the laser cutter made the hole, we can just center the logo engraving (or custom inlay engraving) in the hole to ensure that we are correctly registered. The plywood fixture itself is pushed up into the top right of the work bed so that it remains in a constant location.</p> <h4 id="finding-the-right-power-settings-for-laser-cuttingwood">Finding the right power settings for laser cutting wood</h4> <p>The toughest thing about using a laser cutter to engrave wood is optimizing power settings. We want to perform the work as quickly as possible which ostensibly means using the highest power and fastest speed available. But we need to watch out, as too much power makes the wood burn.</p> <p>There are three variables we can change: power, speed, and throughput. Power and speed are self-explanatory. Throughput is the density of the cut. Low throughput means that the laser cut spends longer making a higher resolution rasterized cut. So low throughput takes 3 minutes while high throughput takes only 15 seconds.</p> <p>We want to start with establishing a minimum throughput that gives us the minimum acceptable quality of the cut we want but at the fastest speed possible. So we start with a low throughput to establish high quality and then move up in throughput until we reach a cut that’s too sparse.</p> <p>Take a look below at this mahogany wood blank to see what happens when wood burns, even subtly. Starting on the right-hand side you can see how the laser cutter’s exhaust system pulls the smoke up and over the wood, leaving a burn scar above the cut.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*r0NZ_7Y2bAfbQxZVrOxjNw.jpeg" alt="" />A mahogany wood blank worked right-to-left, the left-most engraving was chosen for its perfect height match with the shell</p> <p>Starting from right to left:</p> <ul> <li><strong>High power, 45 sec</strong>: notice the burn scars above the logo. That’s caused by the exhaust fan of the laser cutter pulling the burn up. You can reverse the direction of your cut so that you cut from bottom up, “erasing” the burn scar by burning it, and then possibly using a light power setting by the time the laser reaches the top quarter and going over it multiple times. But that’s quite a commitment to shave maybe 15 seconds off, and even then you still might end up with a burn scar.</li> <li><strong>Medium power, 45 sec</strong>: Same time as before but less power means less burn scar. We need to adjust the density (throughput) of the cut now.</li> <li><strong>Low/high throughput, 180 sec/15 sec</strong>: This photo clearly shows the difference between low throughput (high density) burning on the right and a high throughput skipping every 5th line on the left.</li> <li><strong>Slow speed, high power, 45 sec</strong>: I’ve established a desired throughput and now I’m experimenting here with changing the speed but maintaining high enough power. The cut is reasonable but it still burns due to the speed. If we turn up the laser to max speed but turn down the power output, we get a nice cut at a fast speed, but it’s just not deep enough. To fix that we just need to run the cut twice.</li> <li><strong>Medium throughput, medium power, high speed, double run, 35 sec ⨉ 2</strong>: Here we get to a much more reasonable cut. I’m using 20% power with 100% speed and a 5/7 throughput, but it’s still not deep enough.</li> <li><strong>Medium throughput, medium power, slower speed, double run, 60 sec ⨉ 2</strong>: And now we’ve got it. Moving down to a slower speed but at only a medium power we get the cut we want at a reasonable time of 120 seconds.</li> </ul> <p>YMMV on your particular laser system, but this is a good way of narrowing down values until hitting local maximas.</p> <h4 id="problem-at-theedges">Problem at the edges</h4> <p>When a laser cutter cuts through material, the beam diffuses and creates a sloped edge. You can see how this edge can be a problem below.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*sAlHVGYT_8_A832L6zYnPg.jpeg" alt="" /></p> <p>When you try to inlay the shell later, these slopes will cause the edges of the shell to sit higher than the rest of the shell. It would be very easy to crack the shell if you press it in, since it can’t handle the stress of resting on the sloped edge.</p> <p>One way to reduce the sloped edge is to adjust the focus of the laser. Even if you successfuly set the height of the focus, it still might be off by ±0.005”, which is half the height of the shell itself. So it makes sense, once you establish power, speed, and throughout settings, to also attempt a few cuts above and below your laser cutter’s established focal point, in 0.0025” increments, and see if the slope goes away with the adjustment.</p> <p>Alternatively, since the slopes are only at the edges, you can perform additional cuts with the laser, feathering the inner edges, offset by enough distance so that the diffusion of the laser doesn’t show up outside the engraving.</p> <p>This is what I do and it only takes a couple extra seconds to make a few cuts at the edges on one side. Sloped edges do not necessarily happen on every edge, since the grain of the wood can naturally reinforce an edge or make it harder for the laser to make a clean cut. So in my case I only had to feather one edge of the engraving to get the shell to fit perfectly. And because the cuts are inside the engraving, it doesn’t show and the shell fits in at the proper depth.</p> <h4 id="vector-cutting-the-mother-of-pearlshell">Vector cutting the mother of pearl shell</h4> <p>This is the good stuff. Mother of pearl is such a delicate material that it only needs a light power setting. The only variables I messed around with were power and speed, since vector cutting has no throughput component like the rasterized engraving in the wood above.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*C1nHiKmBTsrYQfzwODx5Dg.jpeg" alt="" />Thin mother of pearl needs to be weighted down so it doesn’t get sucked into the exhaust fan</p> <p>Let’s cut the inside edges of the design first. Just as you don’t want to paint yourself into a corner, you want to laser from the inside out. Otherwise your newly free-moving material may not stay in place for those inner cuts.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*eYuV2GGiNW7oeUS31lgPgQ.png" alt="" />Test every inset variation to minimize the space between wood engraving and cut shell. I settled on a 0.001” inset.</p> <p>The Universal laser cutter I used allows you to set colors that applies an order to each cut. I also used a lower power setting for the outside edge cut. Since the entire square is easier to separate that the inner cut, a faster speed cut keeps the mother of pearl marginally connected so that the individual pieces don’t go flying as they get cut.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*VUDgPAwvpputKdWjRCdNrw.gif" alt="" />Hot cutting action</p> <p>I could have run the laser at a higher speed (at a higher power to compensate), but I kept it at a low speed to ensure a quality cut. If the wood engraving isn’t perfect, it matters less than if the shiny shell has rough corners that become a lot more noticeable.</p> <h4 id="adhering-the-wood-and-shelltogether">Adhering the wood and shell together</h4> <p>Once you’ve got the two cut materials, the wood and the shell, you’ll need to get them to stick together. Since there’s no forces acting upon the inlay other than to reinforce it down into the engraving, we don’t need a messy epoxy or expensive shell glue to hold it in.</p> <p>I tried many glues and settled on squeezable Extra Time Control super glue gel. The gel makes application easier, instead of being thin and runny. However, using off-the-shelf super glue (cyanoacrylate) without time control gave me only 5 seconds between application and drying. So placing the shell into the engraving was a one-shot deal, with the shell becoming permanently affixed to the wrong place if you didn’t nail its position the first time.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*lXFsR_Y3n2xLgbf9b7Xu4A.jpeg" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*JzdB3WPvhwQKzmW1yImbCQ.jpeg" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*rQbz_daEuFYq69qrSSb-2A.jpeg" alt="" />Better to apply the glue to the shell than to the wood, less run-off on to the wood that way<img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*0WYMv5Q3UvF-qMXDUMfUTQ.jpeg" alt="" /><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*HlIv9goBBKicMw-qER43kA.jpeg" alt="" />Place and wipe clean</p> <p>Time control or not, it’s still super glue and it adheres within 15 seconds. But because it has time control, any excess glue easily wipes off the wood with a paper towel.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/turntouch/blog/1*4OorUgvWI12PoDnRVWeKCg.jpeg" alt="" />2 minutes of lasering per remote, 30 seconds of glueing, and you get a finished remote</p> <p>That’s it! That’s the last step. We now have a complete Turn Touch ready to ship to backers.</p> <p>If you want to be one of those backers, <a href="http://kickstarter.com/projects/samuelclay/turn-touch-beautiful-control">head to Kickstarter and get your own Turn Touch</a>.</p> <hr /> <p>This is a <a href="/2019/01/3/building-turntouch/">four part series</a> on everything you need to build your own Turn Touch smart remote.</p> <p>If you want to get your own, <a href="https://turntouch.com">Turn Touch is now for sale for only $49</a>.</p>Samuel ClayBack in 2014 I was driving up the 101 coming back from a YC alumni Demo Day and I had a lightbulb moment about the way I wanted to control my new Hue lightbulbs. That’s when the idea of Turn Touch was born. I’d had some experience building open source hardware projects for my home, and developing open source art installations for Burning Man at a slightly larger scale, but I had never thought I’d build an open source hardware project that would be commercially available (or at least available on Kickstarter). Turn Touch is now available to buy for $49 if you don’t feel like building your own. There are four steps to building your very own Turn Touch: Step one: Laying out the buttons and writing the firmware Step two: Designing the remote to have perfect button clicks Step three: CNC machining and fixturing to accurately cut wood Step four: Inlaying the mother of pearlBuilding Grove — interactive trees that come alive to your breath at Burning Man 20162017-03-31T08:17:43+00:002017-03-31T08:17:43+00:00https://ofbrooklyn.com/2017/03/31/building-grove-burning-man-art-installation-2016<p>Grove is a set of 10 interactive biofeedback sculptures, a conversation between humans and trees. Each tree is made of steel tubes, thousands of LEDs, and custom breathing sensors.</p> <p>Grove is a <a href="http://burningman.org/culture/history/art-history/archive/#Grove">2016 honorarium installation at Burning Man</a> and is the second installation made by the group that made <a href="http://www.ofbrooklyn.com/2014/09/6/building-pulse-bloom-biofeedback-burning-man-2014/">Pulse &amp; Bloom</a> in 2014. The core team is ten people: <a href="https://twitter.com/sabarani">Saba Ghole</a>, <a href="https://www.facebook.com/shilosuleman">Shilo Shiv Suleman</a>, <a href="https://twitter.com/thesprky">Severin Smith</a>, <a href="https://www.facebook.com/notstevelyon">Steve Lyon</a>, <a href="https://hscott.net">Hunter Scott</a>, <a href="https://cambridge.nuvustudio.com/david-wang">David Wang</a>, <a href="https://www.facebook.com/coelho.francis">Francis Coelho</a>, <a href="https://www.facebook.com/naeemeh.alavi">Naeemeh Alavi</a>, <a href="http://lukeiseman.com">Luke Iseman</a>, and myself, <a href="https://twitter.com/samuelclay">Samuel Clay</a>. Grove was conceptualized by the artist Shilo Shiv Suleman as part of her larger series exploring nature, intimacy and technology called <a href="http://melaagency.com/artists/shilo-shiv-suleman/">Beloved</a>.</p> <p>Here’s how Grove works: you sit down at the base of a tree and a flower opens up in front of you as it senses your presence. As you breathe into a pink flower lit from inside, the tree fills up with your breath, rising white streams overtaking multiple slowly descending green lines. As you breathe, the tree shimmers with light as it becomes a nighttime desert oasis.</p> <video src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/Grove.mp4" autoplay="" loop="" muted=""></video> <p>This post offers details on how this installation was built, from the custom circuit boards to the blooming flower and breathing sensors. You can access <a href="https://github.com/samuelclay/Grove">the complete source for Grove on GitHub</a>.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-breathing-nighttime.jpg" /></p> <p>Grove is made up of ten trees, each of which has its own breath sensor and set of two thousand LEDs across four 5 meter 144 LEDs/meter LED strips and 16 high current LEDs in the leaves. That takes both a lot of power to run and a lot of manpower to setup. This is what that setup looks like.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-build-wide.JPG" /></p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-party-dancing.jpg" /></p> <h6>This photo and a few others below are by <a href="https://www.facebook.com/laurel.daily">Daily Swa Laurel</a></h6> <h2>How the electronics are made</h2> <p>Since the trees light up with your breath, we need to consider how the breath is sensed and then how the thousands of LEDs are driven.</p> <p>These are five stages to making the electronics work together:</p> <ol> <li><a href="/2017/03/31/building-grove-burning-man-art-installation-2016/#sensor">Designing a breathing sensor</a></li> <li><a href="/2017/03/31/building-grove-burning-man-art-installation-2016/#main">Making the main control board</a></li> <li><a href="/2017/03/31/building-grove-burning-man-art-installation-2016/#dispatcher">Driving high current LEDs with a custom lighting board</a></li> <li><a href="/2017/03/31/building-grove-burning-man-art-installation-2016/#firmware">Writing the firmware</a></li> <li><a href="/2017/03/31/building-grove-burning-man-art-installation-2016/#power">Powering ten trees in Grove</a></li> </ol> <!--more--> <h3 id="sensor">1. Designing a breathing sensor</h3> <p>First things first, we need to focus on how to design a breathing sensor.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-flower-breathe.jpg" /></p> <h4 id="the-plastic-lotus-flower-assembly">The plastic lotus flower assembly</h4> <p>This is the plastic lotus flower assembly built and designed by <a href="https://cambridge.nuvustudio.com/studios/long-term-projects/grove-burning-man#tab-portfolio-url">the NuVu team in Boston</a>. It is supported by a 3 foot gooseneck that is tied to the base of the tree. This allows the person sitting down by the tree to pull the flower, that at this point is lit up and has magically opened itself, over to their mouth.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-flower-open.png" /></p> <p>We placed a pair of <a href="https://www.adafruit.com/product/189">passive IR motion sensors</a> a few feet up the base of the tree looking out on both sides to detect when somebody sits down. That signal then directs the servo motor in the flower to open the flower up and make the LEDs inside the flower pulsate. This beckons the person to pull the flower over to them.</p> <p>The flower has a proximity sensor embedded inside that is more commonly used by paper towel dispensers to detect hand movement. We used the <a href="https://moderndevice.com/product/si1143-proximity-sensors/">Si1143</a>, same as in <a href="http://www.ofbrooklyn.com/2014/09/6/building-pulse-bloom-biofeedback-burning-man-2014/#sensors">the pulse sensor in Pulse &amp; Bloom</a>, to detect proximity. When we detect that a person is positioned directly in front of the flower, we then allow the breath measurements to light up the tree. Otherwise wind would take over and the tree would be constantly lit.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/prototyping-sensor.jpg" /></p> <p>Above you can see the board that is placed inside the flower. There is a trumpet that fits directly over the board and directs airflow over the board. There are two slots in the board that allow your breath to flow over the hot-wire thermistors and out the back of the flower. Otherwise when you breathed into the flower, the flower would send your last breath back out.</p> <p><a href="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-pcb-sensor.png"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-pcb-sensor.png" /></a></p> <p>The sensor board and the main board are driven and controlled by their own Teensy 3.2. I cannot say enough good things about the Teensy. We originally used an 8-bit Arduino Uno compatible ATmega328p chip but found the 8K memory limiting when we wanted to address thousands of LEDs at once.</p> <p><a href="https://www.pjrc.com/teensy/index.html"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-teensy.jpg" /></a></p> <p>The Teensy gives us plenty of head room and it was easy to get a <a href="https://github.com/samuelclay/grove/blob/master/firmware/sensor/Makefile">GCC-based Makefile deployment setup</a> so all we had to do was run one <code>make</code> command and the board was flashed and rebooted.</p> <h4 id="detecting-your-breath">Detecting your breath</h4> <p>Now we get to the heart of the matter. To detect your breathing, we turn to the <a href="https://moderndevice.com/product/wind-sensor/">Modern Device wind sensor</a>. They use what’s called the “hot-wire” technique. This involves heating a couple of thermistors to a constant temperature and then measuring the electrical power required to maintain the heated thermistor at temperature as wind cools it down. The electrical power is directly proportional to the square of the wind speed.</p> <p>You can buy anemometers for hundreds of dollars but we made our own following the Modern Device wind sensor. Hot-wire anemometers excel at low wind speed, which is pretty much the same speed as breathing.</p> <p>While out on the playa we discovered that the plastic trumpet, which is used to channel your breath toward the sensor while keeping out wind, would cover a bit too much of the proximity sensor. We solved this problem by adding ultrasonic sensors (thanks to Frys for letting us buy out their collection of ultrasonic sensors on the way to the burn), pictured below on top of the orange trumpet.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/IMG_9930.jpg" /></p> <p>We spent a couple days adding these ultrasonic sensors into the boards, soldering in the desert and making do with what we had. Thankfully we left a few pins open that we could then use for the new and improved proximity sensors.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/build-severin-soldering.jpg" /></p> <p>You can explore <a href="https://github.com/samuelclay/grove/blob/master/firmware/sensor/src/sensor.cpp">the code we used to sense your breath on Github</a>. There’s a few parts that I’ll highlight below in the <a href="#firmware">writing the firmware section</a>.</p> <h3 id="main">2. Making the main control board</h3> <p>This is the board that talks to the sensor board and the dispatcher board and gets all of the LED strips plugged into it. This is where the main battery connects to the tree and where power is distributed to each of the sensors and LEDs. This board also communicates with the dispatcher board up top in the leaves to make the high current LEDs fade between colors.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-mainboard.jpg" /></p> <p>You’ll notice on the left is an <a href="https://www.aliexpress.com/item/2016-Brand-New-1pcs-Step-down-Power-DC-DC-CC-CV-Buck-Converter-Supply-Module-7/32608517078.html">8A DC-to-DC step-down voltage regulator</a> that brought the 12V down to 5V needed for the LED strips and sensor board. The dispatcher board and its 16 high current LEDs needs the full 12V. We originally used a cheaper 3A voltage regulator, but it turns out that the number of LEDs we had on at any given time would regularly hit 5A, so we had to boost the voltage regulator.</p> <p><a href="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-pcb-main.png"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-pcb-main.png" /></a></p> <p>You should also note that we used a Teensy here as well. There are some protections built into the circuit to prevent burning out the Teensy if we happen to short somewhere. Thankfully this didn’t happen, but we made the Teensy removable and replaceable in case it did.</p> <p>All of the wire-to-board connectors are <a href="https://www.digikey.com/catalog/en/partgroup/mta-100-series/2660">MTA100 Series female</a> and <a href="https://www.digikey.com/catalog/en/partgroup/mta-100-series/9332">male pin headers</a>. They are an inexpensive connector that you can crimp onto AWG 22 gauge wires and then easily insert into 0.1” male header pins. They even include an optional polarity wedge on the side so that you can ensure the wires are always plugged in the right direction, which is especially helpful when you have people helping you out and you want to ensure that no mistakes are even possible.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-crimp.jpg" /></p> <p>Crimping the MTA100 headers to the wires is pretty fast. You can use either a <a href="http://www.newark.com/te-connectivity-amp/59803-1/insertion-tool/dp/98F2821">$30 insertion tool</a> or a <a href="https://www.digikey.com/product-detail/en/te-connectivity-amp-connectors/58074-1/A2031-ND/30197">$100 crimping tool</a> with a <a href="https://www.digikey.com/products/en?mpart=58246-1&amp;v=17">$163 MTA100-specific crimping head</a>.</p> <p>The best part about the MTA100 is that the wires are removable, so if you make a mistake you can just pull the wire out of the crimped head and try again.</p> <p>Sometimes the connections wouldn’t take and they would have to be re-crimped. This happened on a few of the trees. In the future what we would have done is add circuit testing to the mainboard. This would be an in circuit current measurement sensor. On boot we would turn each of the LEDs on individually, sensing whether or not the LEDs are actually drawing current. This way we can identify improperly crimped LEDs (or just plain old broken LEDs) and turn on a status LED on the board to quickly check.</p> <h3 id="dispatcher">3. Driving high current LEDs with a custom lighting board</h3> <p>That brings us to the dispatcher board on top. The goal is to have 16 individual 3W ultra-bright LEDs on top of the tree light up as each breath climbs to the top of the tree. These high current LEDs look like they are effectively breathing, turning your breath into the breath of an entire tree. It’s a magical effect and to pull it off we’re going to need a custom board just to handle these tricky LEDs.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/prototyping-leds.jpg" /></p> <p>Last time with Pulse &amp; Bloom we didn’t have a dispatcher board, instead relying on driving the LEDs with loose wires connected directly to PicoBucks. It was a mess. Here’s the wiring diagram just for the 9 high current LEDs.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Petal%20LEDs%20wiring.png" /></p> <p>This means that there are over a hundred wires (6 per LED) per tree.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20wired%20picobuck.jpg" /></p> <p>We decided instead to make a board that not only could handle all of this wiring mess but also allow us to easily change out the PicoBucks if they shorted, which is something that happened surprisingly often until we realized that the factory solder jobs on the LEDs themselves could sometimes short.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/prototyping-dispatcher.jpg" /></p> <p>You can see from the schematic below that we simplified the setup by hooking up a set of four high current LEDs per PicoBuck. This let us individually address four sets of lights, giving the tree a mesmerizing pattern of lights that would slowly breathe on their own in green, turning blue when filled with “oxygen” from somebody sitting down and breathing into the breathing sensor at the base of the tree.</p> <p><a href="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-pcb-dispatcher.png"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-pcb-dispatcher.png" /></a></p> <p>We also added protection fuses to the dispatcher board in case the LEDs did short. Since each LED draws a quarter amp at 12 V, we needed to make sure that they would be protected.</p> <p>There is also a 12 channel SPI DAC. Since the PicoBucks need an analog signal as input to specify the PWM-enabled brightness on the LEDs, we couldn’t easily send an analog signal up from the main board since it would have to traverse over 5 meters and be subject to massive voltage drop. Instead we sent a digital signal and converted it to analog on top. This was easy and inexpensive to do with the <a href="http://www.digikey.com/product-detail/en/rohm-semiconductor/BH2221FV-E2/BH2221FV-E2CT-ND/1158686">$4 BH2221FV chip from Rohm</a>.</p> <h3 id="firmware">4. Writing the firmware</h3> <p>There are two boards that need their own firmware loaded on to their own Teensy. The <a href="https://github.com/samuelclay/grove/tree/master/firmware/sensor">sensor board</a> and the <a href="https://github.com/samuelclay/grove/tree/master/firmware/main">main board</a> can both be found on Github.</p> <h4 id="the-sensor-board">The sensor board</h4> <p>Let’s go over a few of the more interesting functions that are used to light up your breath.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span> <span class="n">readRemoteState</span><span class="p">();</span> <span class="n">updateLEDs</span><span class="p">();</span> <span class="n">updateFlowerServo</span><span class="p">();</span> <span class="n">updatePIR</span><span class="p">();</span> <span class="n">runWindAvgs</span><span class="p">();</span> <span class="n">updateProx</span><span class="p">();</span> <span class="n">evaluateState</span><span class="p">();</span> <span class="n">runBreathDetection</span><span class="p">();</span> <span class="p">}</span></code></pre></figure> <p>To begin, the sensor board runs through a tight loop looking for the proximity and PIR motion sensors to trigger so that it can begin to read breath measurements. If the proximity sensor is not firing then that means nobody is in front of the front and we should not be showing any of the breath measurements we might be reading due to wind.</p> <p>If we didn’t have a working proximity sensor (which happened to a few trees) then we would actually just be showing the Earth breathing, otherwise known as the wind. The Earth breathes differently than you or me. Instead of long pulses of hot air, the wind shows up as a sustained line of many tiny pulses, since it’s both cooler and smoother than your breath.</p> <p>Next we have a finite state machine determine which state we’re in. The choices are neutral, motion detected, and proximity detected.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">evaluateState</span><span class="p">()</span> <span class="p">{</span> <span class="k">switch</span> <span class="p">(</span><span class="n">overallState</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="n">STATE_NEUTRAL</span><span class="p">:</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">pirState</span> <span class="o">==</span> <span class="n">PIR_ON</span><span class="p">)</span> <span class="p">{</span> <span class="n">overallState</span> <span class="o">=</span> <span class="n">STATE_OPEN</span><span class="p">;</span> <span class="n">openFlower</span><span class="p">();</span> <span class="p">}</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="k">case</span> <span class="n">STATE_OPEN</span><span class="p">:</span> <span class="p">{</span> <span class="kt">long</span> <span class="n">now</span> <span class="o">=</span> <span class="n">millis</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="n">now</span> <span class="o">-</span> <span class="n">openTimeoutLastEvent</span> <span class="o">&gt;</span> <span class="n">openTimeout</span><span class="p">)</span> <span class="p">{</span> <span class="n">overallState</span> <span class="o">=</span> <span class="n">STATE_NEUTRAL</span><span class="p">;</span> <span class="n">closeFlower</span><span class="p">();</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">isProx</span><span class="p">())</span> <span class="p">{</span> <span class="n">overallState</span> <span class="o">=</span> <span class="n">STATE_PROX</span><span class="p">;</span> <span class="p">}</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="k">case</span> <span class="n">STATE_PROX</span> <span class="p">:</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">remoteState</span> <span class="o">==</span> <span class="n">STATE_NEUTRAL</span><span class="p">)</span> <span class="p">{</span> <span class="n">overallState</span> <span class="o">=</span> <span class="n">STATE_NEUTRAL</span><span class="p">;</span> <span class="n">closeFlower</span><span class="p">();</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isProx</span><span class="p">())</span> <span class="p">{</span> <span class="n">overallState</span> <span class="o">=</span> <span class="n">STATE_OPEN</span><span class="p">;</span> <span class="n">breathOff</span><span class="p">();</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">runBreathDetection</span><span class="p">();</span> <span class="p">}</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Using a FSM makes it easy to ensure that we always know where we are, even with three separate sensors that all trigger independently of each other, while ensuring that one does not cause a change in the tree without its predecessors.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">runWindAvgs</span><span class="p">()</span> <span class="p">{</span> <span class="kt">long</span> <span class="n">now</span> <span class="o">=</span> <span class="n">millis</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="n">now</span> <span class="o">-</span> <span class="n">lastWindSampleTime</span> <span class="o">&gt;</span> <span class="n">windSampleInterval</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Currently tuned to sample every 20ms roughly</span> <span class="kt">int</span> <span class="n">raw</span> <span class="o">=</span> <span class="n">getRawWind</span><span class="p">();</span> <span class="n">lowWindAvg</span> <span class="o">=</span> <span class="n">lowWindAvg</span> <span class="o">*</span> <span class="n">lowWindAvgFactor</span> <span class="o">+</span> <span class="n">raw</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">lowWindAvgFactor</span><span class="p">);</span> <span class="n">highWindAvg</span> <span class="o">=</span> <span class="n">highWindAvg</span> <span class="o">*</span> <span class="n">highWindAvgFactor</span> <span class="o">+</span> <span class="n">raw</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">highWindAvgFactor</span><span class="p">);</span> <span class="n">lastBpassWind</span> <span class="o">=</span> <span class="n">bpassWind</span><span class="p">;</span> <span class="n">bpassWind</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)(</span><span class="n">highWindAvg</span><span class="o">-</span><span class="n">lowWindAvg</span><span class="p">);</span> <span class="n">windHistory</span><span class="p">[</span><span class="n">windHistoryIndex</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">bpassWind</span> <span class="o">-</span> <span class="n">lastBpassWind</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">windHistoryIndex</span> <span class="o">&gt;=</span> <span class="n">WIND_HIST_LEN</span><span class="p">)</span> <span class="n">windHistoryIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">lastWindSampleTime</span> <span class="o">=</span> <span class="n">now</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>We use the above <code class="language-plaintext highlighter-rouge">runWindAvgs</code> to average the raw readings and establish a moving average with hysteresis. This means that we want to intentionally avoid rapid switching of states, so we have upper and lower thresholds that move with the average.</p> <h4 id="the-main-control-board">The main control board</h4> <p>Next we come to the main board. Let’s walk through the steps. Instead of showing the code from the main board, I’m going to talk about the various functions. You can view <a href="https://github.com/samuelclay/grove/blob/master/firmware/main/src/grove.cpp">all of the main board’s code on Github</a>.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span> <span class="n">addRandomDrip</span><span class="p">();</span> <span class="n">advanceRestDrips</span><span class="p">();</span> <span class="n">updatePIR</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="n">updatePIR</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="n">transmitSensor</span><span class="p">();</span> <span class="n">receiveSensor</span><span class="p">();</span> <span class="cp">#ifdef RANDOMBREATHS </span> <span class="k">if</span> <span class="p">(</span><span class="n">millis</span><span class="p">()</span> <span class="o">%</span> <span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">5000</span><span class="p">)</span> <span class="p">{</span> <span class="n">addBreath</span><span class="p">();</span> <span class="p">}</span> <span class="cp">#else </span> <span class="k">if</span> <span class="p">(</span><span class="n">proximityState</span> <span class="o">==</span> <span class="n">STATE_PIR_ACTIVE</span> <span class="o">&amp;&amp;</span> <span class="n">detectedBreath</span><span class="p">)</span> <span class="p">{</span> <span class="n">addBreath</span><span class="p">();</span> <span class="p">}</span> <span class="cp">#endif </span> <span class="n">advanceBreaths</span><span class="p">();</span> <span class="n">runLeaves</span><span class="p">();</span> <span class="n">runBase</span><span class="p">();</span> <span class="n">leds</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="p">}</span></code></pre></figure> <p>What’s going to happen is that the tree will randomly add “drips” to the top of the tree.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">addRandomDrip</span><span class="p">();</span> <span class="n">advanceRestDrips</span><span class="p">();</span> </code></pre></figure> <p>These drips are a random length with a random green-tinted color. They will use easing equations to slowly but rhythmically drip down the tree. The effect is that the trees are swimming in their own green waves.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">updatePIR</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="n">updatePIR</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="n">transmitSensor</span><span class="p">();</span></code></pre></figure> <p>When a person sits down at the base of the tree, the PIR motion sensor fires. We then send this signal over to the sensor board, which then opens up the flower using a servo motor and changes the color of the flower.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">receiveSensor</span><span class="p">();</span></code></pre></figure> <p>The sensor then sends back data about the proximity sensor and the detected breaths.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#ifdef RANDOMBREATHS </span><span class="k">if</span> <span class="p">(</span><span class="n">millis</span><span class="p">()</span> <span class="o">%</span> <span class="p">(</span><span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">5000</span><span class="p">)</span> <span class="p">{</span> <span class="n">addBreath</span><span class="p">();</span> <span class="p">}</span> <span class="cp">#else </span><span class="k">if</span> <span class="p">(</span><span class="n">proximityState</span> <span class="o">==</span> <span class="n">STATE_PIR_ACTIVE</span> <span class="o">&amp;&amp;</span> <span class="n">detectedBreath</span><span class="p">)</span> <span class="p">{</span> <span class="n">addBreath</span><span class="p">();</span> <span class="p">}</span> <span class="cp">#endif</span></code></pre></figure> <p>Once a breath is detected, we add the head of the breath to the bottom of the tree and guide it upwards.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">advanceBreaths</span><span class="p">();</span></code></pre></figure> <p>The high current LEDs in the leaves are glowing in randomly paced greens.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">runLeaves</span><span class="p">();</span> <span class="n">runBase</span><span class="p">();</span></code></pre></figure> <p>When the head of the breath reaches the top of the tree, the high current LEDs then begin to glow in a whiteish blue. This signals that the tree is breathing at the same pace as the person sitting underneath it.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">leds</span><span class="p">.</span><span class="n">show</span><span class="p">();</span></code></pre></figure> <p>Since we’ve queued up all of the LEDs, it’s now time to send the data over the line and update the tree for this loop. There will be dozens of loops per second, enough to make the tree’s many LEDs look fluid in their motion.</p> <h4 id="the-dispatcher-board">The dispatcher board</h4> <p>Finally, because we’re using a 12 channel SPI DAC on the dispatcher so that we can send digital signals up the long trunk of the tree to later be converted to analog signals used to set brightness of the high current LEDs, we need to make the simple transactions over SPI.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cm">/** * Set the analog output on the dispatcher board. * Channels are numbered 1 to 12 inclusive (THEY DON'T START AT ZERO !!) * value is 0-255 range analog output of the dac/LED brightness */</span> <span class="kt">void</span> <span class="nf">dispatcher</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">chan</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// We could normalize here to a "real" range</span> <span class="c1">// There's nothing below 45 because below 0.5 the pico buck is off</span> <span class="c1">// value = value * 210 / 255;</span> <span class="c1">// if (value &gt; 0) value += 45;</span> <span class="c1">// take the SS pin low to select the chip:</span> <span class="n">SPI</span><span class="p">.</span><span class="n">beginTransaction</span><span class="p">(</span><span class="n">dispatcherSPISettings</span><span class="p">);</span> <span class="n">digitalWrite</span><span class="p">(</span><span class="n">slaveSelectPin</span><span class="p">,</span> <span class="n">LOW</span><span class="p">);</span> <span class="c1">// send in the address and value via SPI:</span> <span class="n">SPI</span><span class="p">.</span><span class="n">transfer</span><span class="p">(</span><span class="n">flipByte</span><span class="p">((</span><span class="n">chan</span> <span class="o">&amp;</span> <span class="mh">0x0F</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">));</span> <span class="n">SPI</span><span class="p">.</span><span class="n">transfer</span><span class="p">(</span><span class="n">value</span><span class="p">);</span> <span class="c1">// take the SS pin high to de-select the chip:</span> <span class="n">digitalWrite</span><span class="p">(</span><span class="n">slaveSelectPin</span><span class="p">,</span><span class="n">HIGH</span><span class="p">);</span> <span class="n">SPI</span><span class="p">.</span><span class="n">endTransaction</span><span class="p">();</span> <span class="p">}</span></code></pre></figure> <h3 id="power">5. Powering ten trees in Grove</h3> <p>Running an electronics installation out in the desert presents a few power problems. First is that there is nowhere to plug in, so all of the energy you’re going to need, whether it is stored or rechargeable, is going to have to be brought out there on the playa with you.</p> <p>We quickly decided on running the installation on solar-powered deep cycle batteries. The cost for the batteries was mitigated slightly by the fact that we already had a 1.2 kW solar array handy. In past years you could affordably rent solar arrays from non-profits (like Black Rock Solar, who have written about <a href="http://www.blackrocksolar.org/news/2015/solarize-the-playa/">how to solarize your playa art installation</a>), but changes in Nevada’s energy policy have meant that’s no longer an option. It’s kind of tragic and the <a href="https://www.nytimes.com/2016/02/01/opinion/nevadas-solar-bait-and-switch.html">New York Times recently covered how Solar City had to move out of the state</a>.</p> <p>The other option is to use a generator, but while relatively cheap, they are loud and would detract from the serenity of the installation. We could instead choose to run a generator in a baffle box and run AC power over a length 50 meter distance, but that was deemed un-Grove-like and we stuck with batteries.</p> <p>Let’s take a look at what’s on the market.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-batteries.png" style="border: none" /></p> <p>Most batteries cost around 20 cents per Watt-hour. Let’s perform some calculations to figure out how much we need. Let’s look at a single tree. Most of the time a tree is going to be in a “rest” state, where nobody is actively using it. This means the tree is draped in green and the high current LEDs are in a lower power state.</p> <p>About 5% of the time we’re going to be in active mode, with brighter LED colors on the 4 × 5 meter strips and 16 × high current LED colors.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 95% × ((4 LED strips × 1.50A @ 5V per strip) + (0.25A @ 5V base lighting) + (16 × 0.08A @ 12V)) + 5% × ((4 LED strips × 1.75A @ 5V per strip) + (0.25A @ 5V base lighting) + (16 × 0.10A @ 12V)) = 0.95 * (4*1.5*5 + .25*5 + 16*.08*12) + 0.05 * (4*1.75*5 + .25*5 + 16*.1*12) ≈ 47W per tree </code></pre></div></div> <p>At 10 trees, that’s 470 watts of power. We expect the installation to run 10 hours (8pm - 6am). That’s 4,700 Watt-hours. We’re looking at a set of batteries that should cost (4700 Wh × $0.20/Wh) = $940.</p> <p>What we did was put 3 6V 250 Ah batteries in series to boost it up to 18V instead of just using 2 6V batteries at 12V. We did this to limit the DC voltage drop between the battery array and the trees, each of which was between 10 and 20 meters away from the battery array. Since voltage drop is linear with respect to distance, the reduction from 18V to ~15V is a 16% drop but 12V to ~9V is a 25% drop. This prevents the high current LEDs from flickering, since they need a minimum of 10V to work.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/build-solar-array.jpg" /></p> <p>We charged the batteries using Luke’s solar array that he just happened to have handy. Here’s Luke on the challenges we faced:</p> <blockquote> <p>Sam and I had a few 5-minute discussions about how much power we needed, and this was inadequate: our installation often ran out of power around sunrise. I should have brought twice as many batteries and 1.5x the solar panels. By dumb luck, this ‘low power mode’ produced an interesting ‘dead red’ effect: the batteries at lowest levels provided only enough power for the red leds to pulse, leaving out all other colors. Unfortunately, the batteries needed desulfated for a month straight after playa and a few were still killed.</p> </blockquote> <blockquote> <p>In the future, we’ll test <em>the actual power consumption</em> of our installation in the real world: with a kill-a-watt or other power meter, how much is your installation actually drawing over the course of 24 hours? Your battery capacity should be at least 4x this: 2x to keep from discharging more than 50%, and another 2x for when somebody (in our case, me) imbibes too much and forgets to move batteries to solar array until noon.&lt;/blockquote&gt;</p> </blockquote> <h2>Installing the Installation</h2> <p>Before the trees go up we need to assemble them and give them a coat of spray paint. These trees are shaped steel tubes that need some gold applied.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/oakland-trees.jpg" /></p> <p>All of the work is done at American Steel Studios in Oakland, California. We want to do as much work as possible before we get to the playa. Once we’re in the desert, anything we may need is going to be 6 hours round trip to Reno, whereas the Home Depot in Oakland is a 5 minute drive away.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/build-electronics-behind.jpg" /></p> <p>Alas, as much as we try to avoid it, there’s plenty of firmware debugging to be done on the playa.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/build-severin-heather.jpg" /></p> <p>Naturally, firmware debugging often lasted well into the night.</p> <p>Putting the trees up is also a ton of work. We prototyped the leaves at a small scale.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-petals-small.jpg" /></p> <p>But once on the playa we realize it’s a lot more work to attach 16 individual leaves to each of the 10 trees simply as a matter of scale.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-leaf.jpg" /></p> <p>The trees go up a lot faster than individual subassemblies on the tree. This is a good photo of the Pareto principle for art installations. Putting up the first 80% of the installation by volume only took 20% of the time.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/build-trees.jpg" /></p> <h2>Enjoying the fruits of our labor</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-breathing-couple.jpg" /> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-breathing-daytime.jpg" /> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-brittany-breathing-2.jpg" /></p> <p>It’s dusty out there!</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/teardown-tornado.jpg" /></p> <p>And as we did with Pulse &amp; Bloom, these cushions are some of the only seating on the playa, offering a beautiful respite and place to recharge and re-energize from the heat of the playa.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/grove/grove-party-dusty.jpg" /></p> <p>The entire process lasted 3 months and involved 5 people on the electronics team, a half dozen people on fabrication, and another dozen people on assembly and installation. My hope is that others learn from our work and use some of the <a href="https://github.com/samuelclay/grove/">open-source firmware and designs</a> for their own art installations.</p> <p>If you’d like to bring Grove to your art or music festival, please contact <a href="mailto:shilo1221@gmail.com">Shilo Shiv Suleman</a>.</p> <p>I’d love to hear from you if you’re inspired or build your own art installation. <a href="https://twitter.com/samuelclay">Reach me @samuelclay on Twitter</a>.</p> <p><i>breath (n), breathe (v)</i></p>Samuel ClayGrove is a set of 10 interactive biofeedback sculptures, a conversation between humans and trees. Each tree is made of steel tubes, thousands of LEDs, and custom breathing sensors. Grove is a 2016 honorarium installation at Burning Man and is the second installation made by the group that made Pulse &amp; Bloom in 2014. The core team is ten people: Saba Ghole, Shilo Shiv Suleman, Severin Smith, Steve Lyon, Hunter Scott, David Wang, Francis Coelho, Naeemeh Alavi, Luke Iseman, and myself, Samuel Clay. Grove was conceptualized by the artist Shilo Shiv Suleman as part of her larger series exploring nature, intimacy and technology called Beloved. Here’s how Grove works: you sit down at the base of a tree and a flower opens up in front of you as it senses your presence. As you breathe into a pink flower lit from inside, the tree fills up with your breath, rising white streams overtaking multiple slowly descending green lines. As you breathe, the tree shimmers with light as it becomes a nighttime desert oasis. This post offers details on how this installation was built, from the custom circuit boards to the blooming flower and breathing sensors. You can access the complete source for Grove on GitHub. Grove is made up of ten trees, each of which has its own breath sensor and set of two thousand LEDs across four 5 meter 144 LEDs/meter LED strips and 16 high current LEDs in the leaves. That takes both a lot of power to run and a lot of manpower to setup. This is what that setup looks like. This photo and a few others below are by Daily Swa Laurel How the electronics are made Since the trees light up with your breath, we need to consider how the breath is sensed and then how the thousands of LEDs are driven. These are five stages to making the electronics work together: Designing a breathing sensor Making the main control board Driving high current LEDs with a custom lighting board Writing the firmware Powering ten trees in GroveOpen letter to my family in Ohio: I am still coming home for Thanksgiving this year2016-11-21T13:38:10+00:002016-11-21T13:38:10+00:00https://ofbrooklyn.com/2016/11/21/open-letter-my-family-ohio-i-am-still-coming-home-<p>Dear Mom and Pop,</p> <p>This was going to be an awkward year for us from the start. Thanksgiving is normally a time when we, two dozen immigrant Jews and first-generation Americans, come together to eat a turkey stuffed with oranges, served with a side of smoked fish and a dozen competing salads.</p> <p>Before the election, I thought the greatest thing that would divide our dinner table would be my recently adopted vegetarianism. But us Jews are all about dietary restrictions. Eight breadless days of weight loss every year during Passover. Fasting from food and water every year for the day of atonement. Two sets of plates for Shabbat and for everyday. I was certain that refusing meat wouldn’t stop me from feeling embraced and loved at the table.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/open-letter-dinner.jpeg" style="width: 650px; margin: 0 auto;" /></p> <h6 id="a-russian-salad-feast-courtesy-of-my-friend-andrey-petrov">A russian salad feast courtesy of my friend Andrey Petrov</h6> <p>But then last week you voted for Trump. Jewish refugees from the Ukraine now living in Ohio voted for Trump. You have told me several times that Obama is the worst president in our lifetime. And so you voted for Trump.</p> <!--more--> <p>This letter would not have been written if this were just about politics. Both of you have always voted conservatively. And I wouldn’t be this livid if it were just about privatization of health care, or import tariffs, or the unchecked negative externalities of infrastructure spending on energy extraction. The political tide goes in and out. How we run the country makes for a spirited debate over the Thanksgiving dinner table and I welcome our family’s contrary viewpoints. But this is not about politics.</p> <p>Voting for Trump is clearly a vote for hate. I know you voted against Hillary, and not “in favor” of Trump, but the action is the same. Your vote signals to me that you see our fellow citizens as less than human. History shows where this leads and history is not just words in a textbook for us. You are choosing to ignore our past.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/open-letter-baby-jews.jpeg" style="width: 650px; margin: 0 auto;" /></p> <h6 id="donald-trump-says-hed-absolutely-require-muslims-to-register"><a href="http://www.nytimes.com/politics/first-draft/2015/11/20/donald-trump-says-hed-absolutely-require-muslims-to-register/">Donald Trump Says He’d ‘Absolutely’ Require Muslims to Register</a></h6> <p>We would have seven times as many cousins if it weren’t for the Nazis. You helped elect a fascist and are now complicit in what’s going to happen to families both in and outside of our country.</p> <p>It is not unreasonable for people to think that fascists, if given power and an attempt to govern, will moderate their views. But history repeats itself.</p> <div style="overflow: hidden"> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/open-letter-hitler-1.jpeg" style="float: left; width: 260px; margin-right: 24px;" /> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/open-letter-hitler-2.jpeg" style="float:right; width: 320px;" /> </div> <blockquote>“Several reliable, well-informed sources confirmed the idea that Hitler’s anti-Semitism was not so genuine or violent as it sounded, and that he was merely using anti-Semitic propaganda as a bait to catch masses of followers and keep them aroused, enthusiastic, and in line for the time when his organization is perfected and sufficiently powerful to be employed effectively for political purposes.” <br /><br />“You can’t expect the masses to understand or appreciate your finer real aims. You must feed the masses with cruder morsels and ideas like anti-Semitism. It would be politically all wrong to tell them the truth about where you really are leading them.”</blockquote> <p>I take Trump at his hate-filled words. Last time we spoke, you did not. You said he probably wouldn’t be fulfilling any of his promises, like the ones to register Muslims or deport Latinos. However, even if you do believe that his campaign was only hype, you still ignored despicable behavior and rhetoric. Normalizing racism is itself an act of racism.</p> <p>Some positions are binary. You either care about the protection of minorities or you do not. I have friends now who will stand in the same shoes that my grandparents stood in before. And you knew, regardless of your enthusiasm for Trump’s economic policies, that these people would suffer. The increasing number of graffitied swastikas, along with other vile behaviors now edging into the mainstream, are the kind of atrocities your vote was supposed to suppress.</p> <p>You taught me that my highest responsibility as a Jew is to never forget, to fight against discrimination. This was a common refrain told to me during seven years of Hebrew school. I take that responsibility seriously and that is what I am trying to do now. Elie Wiesel said “We must take sides. Neutrality helps the oppressor, never the victim. Silence encourages the tormentor, never the tormented.”</p> <p>I understand why you voted to put Trump in office. It’s just hard for me to forgive you for it. And now is not the time to give bigotry a pass. I’m trying to live by the values you taught me, which is why I am only asking you to take our President at his word. And when he tells you of his bigoted promises to force members of a religion to register, you should be outraged.</p> <p>I am coming home this Thanksgiving and it’s going to be painful, most of all for me. I try to be empathetic with how you feel about the state of our country, but what I do not understand is how you think that voting for a racist and hate-filled demogogue with no political experience is going to promote growth and peace. At best, it will alienate over half of this country, and at worst, history will repeat itself.</p> <p>I am coming home because I want to believe the love that brings us together at Thanksgiving is strong enough to not only include a vegetarian, but also strong enough to allow you to understand my perspective and possibly even change your minds. I need my family with me in this fight.</p> <p>Your son, <br />Samuel</p>Samuel ClayDear Mom and Pop, This was going to be an awkward year for us from the start. Thanksgiving is normally a time when we, two dozen immigrant Jews and first-generation Americans, come together to eat a turkey stuffed with oranges, served with a side of smoked fish and a dozen competing salads. Before the election, I thought the greatest thing that would divide our dinner table would be my recently adopted vegetarianism. But us Jews are all about dietary restrictions. Eight breadless days of weight loss every year during Passover. Fasting from food and water every year for the day of atonement. Two sets of plates for Shabbat and for everyday. I was certain that refusing meat wouldn’t stop me from feeling embraced and loved at the table. A russian salad feast courtesy of my friend Andrey Petrov But then last week you voted for Trump. Jewish refugees from the Ukraine now living in Ohio voted for Trump. You have told me several times that Obama is the worst president in our lifetime. And so you voted for Trump.Building Pulse &amp; Bloom - an interactive biofeedback installation at Burning Man 20142014-09-06T06:00:00+00:002014-09-06T06:00:00+00:00https://ofbrooklyn.com/2014/09/6/building-pulse-bloom-biofeedback-burning-man-2014<p>This was my second year attending Burning Man. Many use Burning Man as a week to detach from their workweek and experience a new life of intense leisure. Not me, I come to Burning Man to build.</p> <p>Pulse &amp; Bloom is a <a href="https://www.burningman.com/installations/art_honor.html">2014 honorarium installation</a>. The core team of 6 people — <a href="https://twitter.com/sabarani">Saba Ghole</a>, <a href="https://twitter.com/shilo1221">Shilo Shiv Suleman</a>, <a href="https://twitter.com/rd108">Rohan Dixit</a>, <a href="https://www.facebook.com/heather.stewart.3388">Heather Stewart</a>, <a href="https://twitter.com/liseman">Luke Iseman</a>, and <a href="https://twitter.com/samuelclay">myself</a> — built 20 interactive lotus flowers made out of steel and rowlux. Each lotus flower ranges from 8 to 18 feet tall, each of which lights up with your pulse. You and another person can put your hands on a couple of Hamsa hands at the base of the lotus flower and your respective heartbeats will light up the flower.</p> <p>We’ve gotten some great press coverage at <a href="https://www.bbc.com/news/in-pictures-29059374">the BBC</a>, <a href="https://www.theguardian.com/artanddesign/gallery/2014/sep/06/photography-new-york-fashion-gaza-islamic-state?CMP=fb_gu">The Guardian</a>, <a href="https://www.theatlantic.com/infocus/2014/09/burning-man-2014/100802/#img07">The Atlantic’s Big Picture</a> <a href="https://www.theatlantic.com/infocus/2014/09/burning-man-2014/100802/#img22">twice</a>, <a href="https://www.cbsnews.com/pictures/burning-man-2014/9/">CBS News</a>, <a href="https://www.nbcnews.com/pop-culture/pop-culture-news/desert-dwellers-burning-man-festival-full-swing-n192541#ember903">NBC News</a>, and <a href="https://on.msnbc.com/1qkUN4c">MSNBC</a>.</p> <p>As usual, the <a href="https://github.com/samuelclay/pulse-bloom">complete source code for Pulse &amp; Bloom is on GitHub</a>.</p> <h3 id="your-heartbeat-in-light">Your heartbeat in light</h3> <p>Here are a couple videos of all twenty lotus flowers in full working order.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20and%20Bloom%2036b.gif" width="650" /></p> <video src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom.mp4" autoplay="" loop="" muted="" width="650"></video> <p>Each lotus flower is blue until a person or two sits down at its base and places their hand on the pulse sensor. You can see the Hamsa hand and its embedded pulse sensor in this shot of my girlfriend Brittany and me working on keeping the electronics going.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Glue%20Gun.jpg" width="650" /></p> <h6>Photo credit Jim Urquhart / Reuters</h6> <p>When a pulse is read, the lotus flower shoots the heartbeat up the stem and into the petals, where it blooms in a brilliant display of amber. When two people’s hands are being measured, both of their heartbeats are shown as two distinct colors.</p> <!--more--> <h2 id="how-the-electronics-are-made">How the electronics are made</h2> <p>There are five stages to making the electronics:</p> <ol> <li><a href="#boards">Making the custom circuit boards</a></li> <li><a href="#sensors">Making the custom pulse sensors</a></li> <li><a href="#leds">Driving the high current LEDs</a></li> <li><a href="#firmware">Writing the firmware</a></li> <li><a href="#power">Powering the lotuses</a></li> </ol> <p><a name="boards"></a></p> <h3 id="1-making-the-custom-circuit-boards">1. Making the custom circuit boards</h3> <p>The circuit board controls all of the lighting, sensors, timing, and debugging sequences. A reliable circuit board couldn’t just be an Arduino and a breadboard. I decided to use <a href="http://www.oshpark.com">OSH Park</a> and <a href="http://www.oshstencils.com">OSH Stencils</a> to fabricate inexpensive circuit boards. I ultimately went through seven revisions while we worked out the final design of the lotuses. This included adding additional lights, additional sensors, controls for EL wire, and better power management using an external buck converter.</p> <p>This board has support for multiple high current petal LEDs driven by digital RGB pins (used for fading colors in and out using PWM) and in multiple voltages. The high current petal LEDs runs on 12V, which comes directly off the battery, the ATmega328p chip and stem LEDs run at 5V, and the pulse sensor runs at 3.3V.</p> <p>To convert 12V to 5V I use <a href="http://www.electrodragon.com/product/better-than-lm2596-dc-dc-step-down-adjustable-power-supply-module/">a $3 switching voltage regulator from Electrodragon</a>. This requires a +/- connection for both 12V in and 5V out. To convert 5V to 3.3V I used a linear voltage regulator soldered directly on the board, since the current requirements of the pulse sensor were around 20mA. The heat dissipated was negligible compared to the rest of the circuit.</p> <p>I also added terminal connections for two 5 meter LEDs that wrapped around the tall lotus stem. We attached two 5 meter LED strips around the stems because when two people put their hands on the two pulse sensors, we need an easy way to show both heartbeats without one winning out over the other. When only one person’s hand in being measured, both 5 meter light strips show the same color, which makes it that much brighter for a single person.</p> <p>Lastly, the two pulse sensors terminate their I2C wires directly on the board. I used two separate I2C channels instead of one to cut down on time. I could easily have put both pulse sensors on the same I2C connections, but that would’ve required both pull-up resistors on the SDA and SCL data lines as well as a rewrite of the timing functions for the sensor.</p> <p>All of these terminations are made using 2.5mm and 3.5mm pitch screw terminals. In hindsight I would have used female JST-SM mounted connectors and ordered custom wires with JST-SM male connectors. I assumed the lowest common denomenator would be bare tinned wire, but all of the bare wires could easily have been switched over to polarized, latching connectors. This would have reduced over 90% of the field work I had to perform on boards, as their wire would fall out of the screw terminals due to not being screwed in with enough force.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20board%20schematic.png" width="650" class="no-border" /></p> <p>The board was laid out to be as big as a pack of gum. All of the 12V traces are thickened and isolated on the right side of the board. They are rated for 3 amps although they would end up only driving 1 amp on the playa.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20board%20layout.png" width="650" class="no-border" /></p> <p>These boards are then stenciled with solder paste and populated with their chips and passive components.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Soldering%20boards.jpg" width="650" /></p> <p>An inexpensive hot plate is used to reflow the solder. The chip is in a TQFP package, which has a 0.5mm pitch between pins. This makes it nearly impossible to hand solder. This stencil and reflow technique works well and my yield stayed high at around 95%.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Reflowing%20boards.jpg" width="650" /></p> <p>Finally, the board is ready to be populated with screw terminals.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Naked%20Board.jpg" width="650" /></p> <p>The final board in a dust-proof enclosure.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20enclosure.jpg" width="650" /></p> <p><a name="sensors"></a></p> <h3 id="2-making-the-custom-pulse-sensors">2. Making the custom pulse sensors</h3> <p>This was both the riskiest piece of the puzzle and also the most fun to build. In order to show your heartbeat on the lotus, I need to have a clear reading of your pulse. There are a fair number of issues with most off-the-shelf heartbeat sensors, including:</p> <ul> <li><strong>Cost</strong>: While a $50 pulse sensor is nice, it’s way out of our budget. We spent $6 in components to make each of our pulse sensors for a total of 40 sensors (two per lotus).</li> <li><strong>Design affordance</strong>: There were a fair number of pulse sensors that required an infrared light on one side of your finger and an infrared detector/receiver on the other. This clamping of your finger wasn’t something we could get away with in the desert. We needed a sensor you could just touch and hold on one side. It needed to be a simple interface so we wouldn’t have to show any instructions.</li> <li><strong>Reliability</strong>: When a sensor gets covered in dust, will it still work? Infrared light, thankfully, passes through a layer of dust and can read the changes in blood flow in your finger. Some options were entirely visual and they would have performed quite poorly out on the playa.</li> </ul> <p>The first prototype I built suffered from the design affordance and reliability issues, although it worked pretty well in ideal conditions.</p> <video src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Sensor%20opamp.mp4" autoplay="" loop="" muted="" width="650"></video> <p>We ended up finding an IC by Silicon Labs called the Si114x, which uses three LEDs — two infrared and one visible — and a small QFN-10 sized light sensor. The original sensor we used was the <a href="http://www.moderndevice.com">Modern Device</a> <a href="http://moderndevice.com/product/pulse-heartbeat-sensor/">pulse sensor</a>, which also provided firmware and ideas for a board layout. The folks at Modern Device were kind enough to <a href="http://moderndevice.com/documentation/using-the-pulse-sensor/">offer advice on how they built their sensor</a> and about how to fix the infrared LED issues we hit. I owe them beers for saving me from many more excruciating hours of trying to build sensors that don’t sense.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20sensor%20schematic.png" width="650" class="no-border" /></p> <p>I built out a small PCB the size of a fingertip.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20sensor%20layout.png" width="650" class="no-border" /></p> <p>This tiny IC is a bit tricky to solder as it has no leads and the pins are extremely close together. My yield dropped to about 80%, but 4 out of every 5 pulse sensors I built worked perfectly the first time. If they failed, I would remount them on the hot plate and try wicking away any solder bridges I could find. This worked only about half the time. After that I gave up and now have a plastic cup full of broken pulse sensors.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Reflowing%20sensors.jpg" width="650" /></p> <p>Finally the sensor is hooked up and a tiny red light is emitted, letting people know where to put their fingers.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Sensor.jpg" width="650" /></p> <p><a name="leds"></a></p> <h3 id="3-driving-the-high-current-leds">3. Driving the high current LEDs</h3> <p>What turned these lotuses from flowers into gems are the high current LEDs mounted in the petals at the top. There are 9 of these high current LEDs per lotus, split into groups of three, each driven by a constant current driver.</p> <p>If you were to look at one of these LEDs from a few feet away, you would immediately see spots in your eyes. They are extremely bright pinpoints of light. The fact that they are also full color and need a heatsink due to their brightness means that you won’t just find these in an easy package ready for plugging in.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Night.jpg" width="650" /></p> <h6>Photo credit Jim Urquhart / Reuters</h6> <p>Normally a single LED is powered by a single constant current driver. This driver can drive up to 1A of current at 12V. Because we were only using a single channel of color (blue) in the rest state, which is where the lotus spent most of its time, we could triple the number of LEDs driven by a single constant current driver.</p> <p>When in use the petals glow amber, which uses two channels (100% red, 50% green) but only for a moment while at peak current. So while the peak current is above what this constant current driver is rated for, it’s only at peak and therefore within our tolerance.</p> <p>Using a Chinese distributor, we bought these Sparkfun high current LEDs for $0.86 each instead of the full $15. However, we also bought 80 of Sparkfun’s PicoBucks to use as our constant current driver. While we could have built this ourselves to save money, buying 80 of something at $15 each is still only $1.2k, while building them ourselves would only have saved a few hundred dollars.</p> <p><a href="https://www.sparkfun.com/products/8718"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20LEDs.jpg" width="300" class="small no-border" /></a> <a href="https://www.sparkfun.com/products/11850"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20PicoBuck.jpg" width="300" class="small no-border" style="margin-right: 0;" /></a></p> <p>We bought 200 high current LEDs, all of which had to have their 6 anode and cathode connections soldered.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Soldering%20leds.jpg" width="650" /></p> <p>In order to connect 9 high current LEDs to the board, we need to drive them in groups of three. Each group has 6 connections: red, green, blue anodes, and red, green, blue cathodes. The wiring diagram for this setup can get a bit rats nesty, but the end result was fairly easy to work with, as the picobuck groups didn’t cross.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Petal%20LEDs%20wiring.png" width="650" class="no-border" /></p> <p>Finally, a shot of a complete picobuck with the wire connectors in the background.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20wired%20picobuck.jpg" width="650" /></p> <p><a name="firmware"></a></p> <h3 id="4-writing-the-firmware">4. Writing the firmware</h3> <p>The <a href="https://github.com/samuelclay/pulse-bloom">complete source code for Pulse &amp; Bloom is on GitHub</a>. There you can find the firmware used to program the main circuit board’s ATmega328p chip, as well as designs for the laser cut cowl that sits over the pulse sensor’s Si1143x IC.</p> <p>The easiest way to read the firmware is to dive into <code class="language-plaintext highlighter-rouge">src/pulse.cpp</code>. It has all of the pulse and rest states and handles the high level logic.</p> <p>The state machine that runs the entire routine is determined by a couple of critical measurements. The program takes a measurement of both pulse sensors and determines whether a finger is covering either of them and whether or not that finger has an active pulse going.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// Modes</span> <span class="kt">void</span> <span class="nf">determinePlayerMode</span><span class="p">();</span> <span class="kt">void</span> <span class="nf">determineFingerMode</span><span class="p">(</span><span class="kt">int</span> <span class="n">sensor1On</span><span class="p">,</span> <span class="kt">int</span> <span class="n">sensor2On</span><span class="p">);</span> <span class="kt">void</span> <span class="nf">resetStem</span><span class="p">(</span><span class="n">PulsePlug</span> <span class="o">*</span><span class="n">pulse</span><span class="p">);</span> <span class="kt">uint8_t</span> <span class="nf">adjustBpm</span><span class="p">(</span><span class="n">PulsePlug</span> <span class="o">*</span><span class="n">pulse</span><span class="p">);</span></code></pre></figure> <p>When there are no fingers on either of the pulse sensors, the program runs the rest state, which is a blue-topped lotus with a small blue snake running up and down the stem. The program is merely waiting for somebody to put their finger on a sensor.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// State: resting</span> <span class="kt">void</span> <span class="nf">runResting</span><span class="p">();</span> <span class="kt">void</span> <span class="nf">runRestStem</span><span class="p">();</span> <span class="kt">void</span> <span class="nf">runRestStem</span><span class="p">(</span><span class="n">PulsePlug</span> <span class="o">*</span><span class="n">pulse</span><span class="p">,</span> <span class="kt">int16_t</span> <span class="n">currentLed</span><span class="p">);</span> <span class="kt">void</span> <span class="nf">clearStemLeds</span><span class="p">(</span><span class="n">PulsePlug</span> <span class="o">*</span><span class="n">pulse</span><span class="p">);</span> <span class="kt">void</span> <span class="nf">beginPetalResting</span><span class="p">();</span> <span class="n">bool</span> <span class="nf">runPetalResting</span><span class="p">();</span></code></pre></figure> <p>The firmware for the pulse sensor originally came from <a href="http://moderndevice.com/product/pulse-heartbeat-sensor/">Modern Device’s pulse sensor</a>. Before a heartbeat is shown an accurate measurement of a heartbeat needs to be read. In order to determine whether a heartbeat is found and at what speed, a number of light detection measurements are taken and analyzed.</p> <p>Modern Device’s firmware simply finds the high frequency curve (the heartbeat) and the low frequency curve (the light sensors leveling out) and then use a simple peak and valley hueristic to determine the moment a heartbeat begins. This can take up to two or three seconds at first, so a bit of unwinding animation prepares you when you first put your finger on the sensor. I modified the firmware to account for multiple pulses on the same lotus and to have thresholds that worked better for the desert.</p> <p>When a finger is detected, the stem snake lights shoot backwards up and down the lotus in prep for a heartbeat. This looks seamless as it clears the stem. This way a heartbeat doesn’t interrupt the rest state.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// State: end resting</span> <span class="kt">void</span> <span class="nf">beginSplittingStem</span><span class="p">();</span> <span class="kt">void</span> <span class="nf">runSplittingStem</span><span class="p">();</span> <span class="kt">void</span> <span class="nf">runSplittingStem</span><span class="p">(</span><span class="n">PulsePlug</span> <span class="o">*</span><span class="n">pulse</span><span class="p">,</span> <span class="kt">int16_t</span> <span class="n">currentLed</span><span class="p">);</span></code></pre></figure> <p>When a heartbeat is detected, it is first shot up the stem. The color is determined by how many pulse sensors are being used. 1 heartbeat shows amber. 2 heartbeats show in white and light red.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// State: stem rising</span> <span class="n">bool</span> <span class="nf">runStemRising</span><span class="p">(</span><span class="n">PulsePlug</span> <span class="o">*</span><span class="n">pulse</span><span class="p">,</span> <span class="n">PulsePlug</span> <span class="o">*</span><span class="n">shadowPulse</span><span class="p">);</span></code></pre></figure> <p>Once the heartbeat reaches the top of the stem, the petals then fill with light.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// State: petal rising</span> <span class="kt">void</span> <span class="nf">beginPetalRising</span><span class="p">();</span> <span class="n">bool</span> <span class="nf">runPetalRising</span><span class="p">();</span></code></pre></figure> <p>After a set delay, the petal slowly lose light while waiting for the next heartbeat.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// State: petal falling</span> <span class="kt">void</span> <span class="nf">beginPetalFalling</span><span class="p">();</span> <span class="n">bool</span> <span class="nf">runPetalFalling</span><span class="p">();</span></code></pre></figure> <p>Lots of debugging messages have been left in the code and are accessible using the Serial pin on the board. Just turn on <code class="language-plaintext highlighter-rouge">USE_SERIAL</code>, which is commented out at top. Note that this increases binary code size by about 2.5K. I wrote a lot of logging.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="c1">// Debugging</span> <span class="kt">void</span> <span class="nf">printHeader</span><span class="p">();</span> <span class="kt">void</span> <span class="nf">blink</span><span class="p">(</span><span class="kt">int</span> <span class="n">loops</span><span class="p">,</span> <span class="kt">int</span> <span class="n">loopTime</span><span class="p">,</span> <span class="n">bool</span> <span class="n">half</span><span class="p">);</span> <span class="kt">int</span> <span class="nf">freeRam</span> <span class="p">();</span></code></pre></figure> <p><a name="power"></a></p> <h3 id="5-powering-the-lotuses">5. Powering the lotuses</h3> <p>While much of what I built above came out of knowledge I already had, learning how to power this large of an installation off just a battery was all new to me. I’m used to microelectronics that run off a coin cell or AA batteries.</p> <h4 id="measuring-current-and-power-usage">Measuring current and power usage</h4> <p>First, in order to figure out how big of a battery we needed, we had to measure how much power we were taking. I hooked up an ammeter in series with a single lotus to measure the current draw. In rest state, a single lotus drew 0.75A at 12V. In the active pulsing heartbeat state, a single lotus drew 1.5A at 12V. Since a lotus spends 95% of its time at rest, we rounded the average current consumption to 1A.</p> <p>Twenty lotuses at 1A at 12V meant we needed 240Ah to sustain a 12 hour night. A car battery was recommended, but car batteries can only discharge down to 80% without irreparable harm. However, a marine deep-cycle battery can go down to 20% without harm.</p> <p><a href="http://www.lifelinebatteries.com/marineflyer.php?id=6"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20battery.jpg" width="300" class="no-border" /></a></p> <p>This 150 lb. battery has enough capacity, although we ended up adding a couple 6V batteries in series (to boost them to 12V) and then running them in parallel to offset some of the load.</p> <h4 id="dealing-with-the-voltage-drop">Dealing with the voltage drop</h4> <p>A big issue we ran into was the voltage drop across the power lines. We placed the batteries on the edge of the installation, which meant some of the lotuses had 20 meters of wire between them and the battery. The voltage drop per 10 meter section is nearly 2V, which means that the voltage reaching the further lotuses was down to 7.5V.</p> <p>The high current LEDs were not able to be smoothly driven at this low of a voltage, causing them to flicker. We determined that a voltage drop was responsible for the flickering, which got worse the further the lotus sat from the batteries. But when we discovered this the wires had already been trenched 6 inches beneath the playa. A day of work lost, but we sucked it up and ripped out all of the ground wiring to accommodate the battery which had to be moved into the center of the installation.</p> <p>At the end of each night we had to bike out with a trike and carry all 300 lbs. of batteries back to camp, where they were hooked up to a 1 kW solar array, where they were charged at 48A over 6 hours.</p> <h2 id="prototyping-the-lotuses">Prototyping the lotuses</h2> <p>Back at American Steel in Oakland, the lotus stems, bases, and petals were ready to be hooked up. These twenty lotus stems were bent by Heather. They range from 6ft to 16ft in length and each weighs 50 lbs.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Raw%20Stems.jpg" width="650" /></p> <p>Such a heavy stem needs a larger base and heavy anchors to stay upright in the wind.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Base.jpg" width="650" /></p> <p>We placed the lily pad over the base to cover it up and then added all of the electronics. Lights, sensors, and circuit boards all had to be mounted.</p> <p>This is a photo of our first complete lotus flower. Just compare its sheer size to Shilo.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20First%20prototype.jpg" width="650" /></p> <p>Once we were confident that one lotus would successfully light we left the other 19 lotuses packed away for assembly on the playa.</p> <h2 id="a-dusty-scene">A dusty scene</h2> <p>We drove out in the biggest rental car I could get my hands on. Our minivan is full of electronics, bicycles, tents, costumes, coolers full of fruit and veggies, dry snacks, and water.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Van.jpg" width="650" /></p> <p>Since we were early arrival, they give you these red bracelets. They say <em>Work hard, don’t do stupid shit</em>.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Bracelet.jpg" width="650" /></p> <p>The boards would accumulate dust on them everyday. Our plastic enclosures turned out to be too big for the holes we made in the platform. So we ended up using ziploc bags. These baggies stayed attached, but the only reason they didn’t cause any issues is that the boards worked just fine in the dust, as you can see here.</p> <p>If dust was a real problem for the boards, then I would have spent a whole lot more time making a tight fitting enclosure and a hole for it that protects it both from the elements and from people.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Dusty%20Board.jpg" width="650" /></p> <p>The playa gets covered in dust storms regularly throughout the week. A particularly nasty dust storm is pictured here, eating our poor lotus flowers alive.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Dust.jpg" width="650" /></p> <p>Although during the day when its not dust storming the lotuses offer a cozy respite from the heat of the playa.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Pulse%20%26%20Bloom%20-%20Day.jpg" width="650" /></p> <h6>Photo credit Jim Urquhart / Reuters</h6> <p>The entire process lasted 4 months with 2.5 months of nearly full-time work on my part. I’m thrilled that I get to open-source both the process and the firmware. I hope others who build art installations can use some of my lessons learned here. I’m happy to <a href="https://twitter.com/samuelclay">answer any questions on Twitter</a>.</p> <p>See you next year on the playa!</p>Samuel ClayThis was my second year attending Burning Man. Many use Burning Man as a week to detach from their workweek and experience a new life of intense leisure. Not me, I come to Burning Man to build. Pulse &amp; Bloom is a 2014 honorarium installation. The core team of 6 people — Saba Ghole, Shilo Shiv Suleman, Rohan Dixit, Heather Stewart, Luke Iseman, and myself — built 20 interactive lotus flowers made out of steel and rowlux. Each lotus flower ranges from 8 to 18 feet tall, each of which lights up with your pulse. You and another person can put your hands on a couple of Hamsa hands at the base of the lotus flower and your respective heartbeats will light up the flower. We’ve gotten some great press coverage at the BBC, The Guardian, The Atlantic’s Big Picture twice, CBS News, NBC News, and MSNBC. As usual, the complete source code for Pulse &amp; Bloom is on GitHub. Your heartbeat in light Here are a couple videos of all twenty lotus flowers in full working order. Each lotus flower is blue until a person or two sits down at its base and places their hand on the pulse sensor. You can see the Hamsa hand and its embedded pulse sensor in this shot of my girlfriend Brittany and me working on keeping the electronics going. Photo credit Jim Urquhart / Reuters When a pulse is read, the lotus flower shoots the heartbeat up the stem and into the petals, where it blooms in a brilliant display of amber. When two people’s hands are being measured, both of their heartbeats are shown as two distinct colors.Adventures in Wearable Electronics - Making a Light-up Dress2014-01-15T11:07:50+00:002014-01-15T11:07:50+00:00https://ofbrooklyn.com/2014/01/15/adventures-in-wearable-electronics-light-up-dress<table> <tr> <td> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Hoolahoop%202.gif" width="130" height="230" /> </td> <td> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Sparkles.gif" width="130" height="230" /> </td> <td> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Raindrops.gif" width="130" height="230" /> </td> <td> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Spiral.gif" width="130" height="230" /> </td> </tr> </table> <p>For New Year’s Eve 2014, my <a href="http://twitter.com/brittanymorgan">girlfriend</a> and I went to a dance party where wearable electronics were not only encouraged but also on display from a variety of hobbyists. I decided to use this as an opportunity to combine two of my favorite hobbies: sewing and electronics.</p> <p>It is my goal to encourage more people to weave wearable electronics into their own clothing. It’s the future and we might as well look the part. Plus it’s easy to get started and to modify existing code.</p> <p>The <a href="https://gist.github.com/samuelclay/8276775">full source code for this dress</a> is available on GitHub.</p> <h2 id="hardware">Hardware</h2> <table> <tr> <td> <a href="http://www.adafruit.com/products/659"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/flora.jpg" width="200" height="149" class="OB-hardware" /></a> </td> <td> <a href="https://www.sparkfun.com/products/12025"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/ledstrip.jpg" width="200" height="149" class="OB-hardware" /></a> </td> <td> <a href="http://www.adafruit.com/products/727?gclid=CMGI5d3C6LsCFZKGfgod50gASw"><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/batteries.jpg" width="200" height="149" class="OB-hardware" /></a> </td> </tr> </table> <p>I attached six <a href="https://www.sparkfun.com/products/12025">addressable LED strands from Sparkfun</a> ($20 each) to the lining of Brittany’s dress, and then used a <a href="http://www.adafruit.com/products/659">Flora module from Adafruit</a> ($25) to control them. I then used a <a href="http://www.adafruit.com/products/727?gclid=CMGI5d3C6LsCFZKGfgod50gASw">3 x AAA battery holder from Adafruit</a> ($2).</p> <h2 id="setup">Setup</h2> <p>I used Adafruit’s <a href="http://learn.adafruit.com/adafruit-neopixel-uberguide">NeoPixel library</a> to control the LEDs. There were 60 LEDs per 1 meter-long strand. We only needed 40 of the LEDs, but instead of cutting them off, we simply sewed the unused third underneath the strand and cut the software off at 40 LEDs. This way we can repurpose the LED strands when we decide to move them to a new dress.</p> <p>In order to make the connections between the LED strands and the Flora module, I used <a href="https://www.sparkfun.com/products/8186">30 AWG wire</a>, which is an extremely thin and light wire. The gauge is 0.01” and is as fine as thread. This allowed me to sew the wire into the fabric. I could have used conductive thread, but this wire wrap has a sheath that prevents it from shorting other wires when they touch. It’s also extremely light-weight, so having 18 wires (3 wires per LED strand: power, ground, data) looping around the dress wasn’t an issue.</p> <p>I also want to mention that the code below is hard-coded for six stands. There is a fine line between a hack and a project, and for this, due to my limited time budget, was closer to hack than reusable project. You can easily abstract the code below to account for more or fewer strands, but I was able to ship before midnight on NYE, so I’m considering it a success.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// To ensure that the first random color in `loop` is never the same</span> <span class="n">randomSeed</span><span class="p">(</span><span class="n">analogRead</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span> <span class="n">led_a</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">led_b</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">led_c</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">led_d</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">led_e</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">led_f</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">clearLEDs</span><span class="p">(</span><span class="nb">true</span><span class="p">);</span> <span class="p">}</span> <span class="kt">void</span> <span class="nf">loop</span><span class="p">()</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">2</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">hulahoop</span><span class="p">(</span><span class="n">randomColor</span><span class="p">(),</span> <span class="n">random</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span><span class="mi">60</span><span class="p">));</span> <span class="p">}</span> <span class="n">sparkle</span><span class="p">(</span><span class="n">randomColor</span><span class="p">(),</span> <span class="n">random</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span><span class="mi">70</span><span class="p">));</span> <span class="n">raindrops</span><span class="p">(</span><span class="n">randomColor</span><span class="p">(),</span> <span class="n">random</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span><span class="mi">60</span><span class="p">));</span> <span class="n">spiral</span><span class="p">(</span><span class="n">randomColor</span><span class="p">(),</span> <span class="n">random</span><span class="p">(</span><span class="mi">15</span><span class="p">,</span><span class="mi">30</span><span class="p">));</span> <span class="p">}</span></code></pre></figure> <p>Above we have the code for <code class="language-plaintext highlighter-rouge">setup</code> and <code class="language-plaintext highlighter-rouge">loop</code>, the two main Arduino routines. Notice that I am repeating the hula hoop routine, since it’s pretty quick and looks good on repeat.</p> <p>I also want to note that every single routine gets its own random color and random delay. This bit of randomness is something I weave into all of my wearable electronics, since it goes just a bit further than off-the-shelf components and shows that there was some intelligence behind the routine.</p> <p>By giving a random delay to each routine I am actually changing the speed of the routine. Sometimes the raindrops would fall quickly, sometimes the hula hoop would have a slow fall and rise. It’s all part of making mesmerizing patterns.</p> <!--more--> <h2 id="hula-hoop">Hula hoop</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Hoolahoop%202.gif" width="228" height="405" class="OB-full" /></p> <p>For the hula hoop routine, I chose to use cylons at equal positions that move up and down the strand. To make the transition from one LED to the next, I use a cylon, which is a fancy way of saying that the LEDs immediately above and below the active LED are also on, but at a reduced brightness.</p> <p>In this case the cylon is 5 LEDs wide. 100% brightness for the middle/active LED, 1/18th brightness for the two adjacent LEDs, and 1/36th brightness for the two LEDs further out from there.</p> <p>So what you see to the left are actually 5 LEDs turned on per-strand, although they get quite dim as you get further from the active LED. You can adjust the dimness of the adjacent LEDs by adjusting the <code class="language-plaintext highlighter-rouge">weight</code> parameter.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">hulahoop</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">color</span><span class="p">,</span> <span class="n">byte</span> <span class="n">wait</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// weight determines how much lighter the outer "eye" colors are</span> <span class="k">const</span> <span class="n">byte</span> <span class="n">weight</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> <span class="c1">// It'll be easier to decrement each of these colors individually</span> <span class="c1">// so we'll split them out of the 24-bit color value</span> <span class="n">byte</span> <span class="n">red</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0xFF0000</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">byte</span> <span class="n">green</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0x00FF00</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">byte</span> <span class="n">blue</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0x0000FF</span><span class="p">);</span> <span class="c1">// Start at closest LED, and move to the outside</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">LED_COUNT</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">clearLEDs</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="c1">// Now set two eyes to each side to get progressively dimmer</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">byte</span> <span class="n">redWJ</span> <span class="o">=</span> <span class="n">red</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="n">byte</span> <span class="n">greenWJ</span> <span class="o">=</span> <span class="n">green</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="n">byte</span> <span class="n">blueWJ</span> <span class="o">=</span> <span class="n">blue</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span><span class="p">)</span> <span class="p">{</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="n">led_a</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_b</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_c</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_d</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_e</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_f</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">delay</span><span class="p">(</span><span class="n">wait</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Now we go back to where we came. Do the same thing.</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="n">LED_COUNT</span><span class="o">-</span><span class="mi">2</span><span class="p">;</span> <span class="n">i</span><span class="o">&gt;=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span> <span class="n">clearLEDs</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="c1">// Now set two eyes to each side to get progressively dimmer</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">byte</span> <span class="n">redWJ</span> <span class="o">=</span> <span class="n">red</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="n">byte</span> <span class="n">greenWJ</span> <span class="o">=</span> <span class="n">green</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="n">byte</span> <span class="n">blueWJ</span> <span class="o">=</span> <span class="n">blue</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span><span class="p">)</span> <span class="p">{</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="n">led_a</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_b</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_c</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_d</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_e</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_f</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">delay</span><span class="p">(</span><span class="n">wait</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <h2 id="sparkles">Sparkles</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Sparkles.gif" width="228" height="405" class="OB-full" /></p> <p>This is by far the easiest routine to program yet the one that brought the most attention. It simply flashes a random LED across all of the LED strips.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">sparkle</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">color</span><span class="p">,</span> <span class="kt">uint8_t</span> <span class="n">wait</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">LED_COUNT</span> <span class="o">*</span> <span class="n">STRIP_COUNT</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">clearLEDs</span><span class="p">(</span><span class="nb">true</span><span class="p">);</span> <span class="kt">int</span> <span class="n">strip</span> <span class="o">=</span> <span class="n">floor</span><span class="p">(</span><span class="n">random</span><span class="p">(</span><span class="n">STRIP_COUNT</span><span class="p">));</span> <span class="kt">int</span> <span class="n">led</span> <span class="o">=</span> <span class="n">floor</span><span class="p">(</span><span class="n">random</span><span class="p">(</span><span class="n">LED_COUNT</span><span class="p">));</span> <span class="k">switch</span> <span class="p">(</span><span class="n">strip</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">led</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_a</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">1</span><span class="p">:</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">led</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">2</span><span class="p">:</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">led</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">3</span><span class="p">:</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">led</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">4</span><span class="p">:</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">led</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">5</span><span class="p">:</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">led</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="n">delay</span><span class="p">(</span><span class="n">wait</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <h2 id="raindrops">Raindrops</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Raindrops.gif" width="228" height="405" class="OB-full" /></p> <p>This is by far the most difficult of the routines. This one sends down cylons (5 LEDs, with the center LED being the brightest and the adjacent LEDs becoming decreasingly bright, so as to give the impression of smoother animation).</p> <p>But you should notice that the next raindrop starts when the previous raindrop is half-way down the dress. In order to make it seamless, at the beginning of the routine I actually start another LED at the half-way mark, referred to in the below code as <code class="language-plaintext highlighter-rouge">e_alt</code>. This means that there is no point at which the dress looks spent and is waiting for the routine to start over.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">raindrops</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">color</span><span class="p">,</span> <span class="n">byte</span> <span class="n">wait</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// weight determines how much lighter the outer "eye" colors are</span> <span class="k">const</span> <span class="n">byte</span> <span class="n">weight</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> <span class="c1">// It'll be easier to decrement each of these colors individually</span> <span class="c1">// so we'll split them out of the 24-bit color value</span> <span class="n">byte</span> <span class="n">red</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0xFF0000</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">byte</span> <span class="n">green</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0x00FF00</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">byte</span> <span class="n">blue</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0x0000FF</span><span class="p">);</span> <span class="kt">double</span> <span class="n">sludge</span> <span class="o">=</span> <span class="mi">0</span><span class="p">.</span><span class="mi">5</span><span class="p">;</span> <span class="kt">double</span> <span class="n">a_offset</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="kt">double</span> <span class="n">b_offset</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="kt">double</span> <span class="n">c_offset</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="kt">double</span> <span class="n">d_offset</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="kt">double</span> <span class="n">e_offset</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span> <span class="kt">double</span> <span class="n">f_offset</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="c1">// Start at closest LED, and move to the outside</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="p">(</span><span class="n">STRIP_COUNT</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">sludge</span><span class="o">+</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">clearLEDs</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="kt">double</span> <span class="n">n</span> <span class="o">=</span> <span class="n">i</span> <span class="o">%</span> <span class="p">(</span><span class="kt">int</span><span class="p">)(</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="p">(</span><span class="n">STRIP_COUNT</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">sludge</span><span class="o">-</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="n">sludge</span><span class="p">);</span> <span class="kt">double</span> <span class="n">led_count</span> <span class="o">=</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">LED_COUNT</span><span class="p">;</span> <span class="n">bool</span> <span class="n">a_on</span> <span class="o">=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">a_offset</span><span class="o">*</span><span class="n">led_count</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">a_offset</span><span class="o">*</span><span class="n">led_count</span><span class="o">+</span><span class="n">led_count</span><span class="p">);</span> <span class="n">bool</span> <span class="n">b_on</span> <span class="o">=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">b_offset</span><span class="o">*</span><span class="n">led_count</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">b_offset</span><span class="o">*</span><span class="n">led_count</span><span class="o">+</span><span class="n">led_count</span><span class="p">);</span> <span class="n">bool</span> <span class="n">c_on</span> <span class="o">=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">c_offset</span><span class="o">*</span><span class="n">led_count</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">c_offset</span><span class="o">*</span><span class="n">led_count</span><span class="o">+</span><span class="n">led_count</span><span class="p">);</span> <span class="n">bool</span> <span class="n">d_on</span> <span class="o">=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">d_offset</span><span class="o">*</span><span class="n">led_count</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">d_offset</span><span class="o">*</span><span class="n">led_count</span><span class="o">+</span><span class="n">led_count</span><span class="p">);</span> <span class="n">bool</span> <span class="n">e_on</span> <span class="o">=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">e_offset</span><span class="o">*</span><span class="n">led_count</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">e_offset</span><span class="o">*</span><span class="n">led_count</span><span class="o">+</span><span class="n">led_count</span><span class="p">);</span> <span class="n">bool</span> <span class="n">e_alt</span><span class="o">=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">a_offset</span><span class="o">*</span><span class="n">led_count</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">a_offset</span><span class="o">*</span><span class="n">led_count</span><span class="o">+</span><span class="n">led_count</span><span class="o">*</span><span class="n">sludge</span><span class="p">);</span> <span class="n">bool</span> <span class="n">f_on</span> <span class="o">=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">f_offset</span><span class="o">*</span><span class="n">led_count</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">sludge</span><span class="o">*</span><span class="n">f_offset</span><span class="o">*</span><span class="n">led_count</span><span class="o">+</span><span class="n">led_count</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">a_on</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">b_on</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">c_on</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">d_on</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">e_on</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">f_on</span><span class="p">)</span> <span class="p">{</span> <span class="n">clearLEDs</span><span class="p">(</span><span class="nb">true</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="n">n</span><span class="o">-</span><span class="n">a_offset</span><span class="o">*</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="n">sludge</span><span class="p">;</span> <span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="n">n</span><span class="o">-</span><span class="n">b_offset</span><span class="o">*</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="n">sludge</span><span class="p">;</span> <span class="kt">int</span> <span class="n">c</span> <span class="o">=</span> <span class="n">n</span><span class="o">-</span><span class="n">c_offset</span><span class="o">*</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="n">sludge</span><span class="p">;</span> <span class="kt">int</span> <span class="n">d</span> <span class="o">=</span> <span class="n">n</span><span class="o">-</span><span class="n">d_offset</span><span class="o">*</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="n">sludge</span><span class="p">;</span> <span class="kt">int</span> <span class="n">e</span> <span class="o">=</span> <span class="n">n</span><span class="o">-</span><span class="n">e_offset</span><span class="o">*</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="n">sludge</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">e_alt</span><span class="p">)</span> <span class="p">{</span> <span class="n">e</span> <span class="o">=</span> <span class="n">a</span><span class="o">+</span><span class="p">(</span><span class="n">LED_COUNT</span><span class="o">/</span><span class="mi">2</span><span class="p">);</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">f</span> <span class="o">=</span> <span class="n">n</span><span class="o">-</span><span class="n">f_offset</span><span class="o">*</span><span class="n">LED_COUNT</span><span class="o">*</span><span class="n">sludge</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">a_on</span><span class="p">)</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">b_on</span><span class="p">)</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">c_on</span><span class="p">)</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">d_on</span><span class="p">)</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">e_on</span> <span class="o">||</span> <span class="n">e_alt</span><span class="p">)</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">f_on</span><span class="p">)</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">red</span><span class="p">,</span> <span class="n">green</span><span class="p">,</span> <span class="n">blue</span><span class="p">);</span> <span class="c1">// Now set two eyes to each side to get progressively dimmer</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">byte</span> <span class="n">redWJ</span> <span class="o">=</span> <span class="n">red</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="n">byte</span> <span class="n">greenWJ</span> <span class="o">=</span> <span class="n">green</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="n">byte</span> <span class="n">blueWJ</span> <span class="o">=</span> <span class="n">blue</span><span class="o">/</span><span class="p">(</span><span class="n">weight</span><span class="o">*</span><span class="n">j</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">a_on</span><span class="p">)</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">a</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">b</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">b_on</span><span class="p">)</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">b</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">c</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">c_on</span><span class="p">)</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">c</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">d</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">d_on</span><span class="p">)</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">d</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">e_on</span><span class="p">)</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">e</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="o">-</span><span class="n">j</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">f_on</span><span class="p">)</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">f</span><span class="o">-</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span> <span class="o">&amp;&amp;</span> <span class="n">a_on</span><span class="p">)</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">a</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">b</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span> <span class="o">&amp;&amp;</span> <span class="n">b_on</span><span class="p">)</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">b</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">c</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span> <span class="o">&amp;&amp;</span> <span class="n">c_on</span><span class="p">)</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">c</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">d</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span> <span class="o">&amp;&amp;</span> <span class="n">d_on</span><span class="p">)</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">d</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span> <span class="o">&amp;&amp;</span> <span class="n">e_on</span><span class="p">)</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">e</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">f</span><span class="o">-</span><span class="n">j</span> <span class="o">&lt;=</span> <span class="n">LED_COUNT</span> <span class="o">&amp;&amp;</span> <span class="n">f_on</span><span class="p">)</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">f</span><span class="o">+</span><span class="n">j</span><span class="p">,</span> <span class="n">redWJ</span><span class="p">,</span> <span class="n">greenWJ</span><span class="p">,</span> <span class="n">blueWJ</span><span class="p">);</span> <span class="p">}</span> <span class="n">led_a</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_b</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_c</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_d</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_e</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_f</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">delay</span><span class="p">(</span><span class="n">wait</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <h2 id="spiral">Spiral</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Spiral.gif" width="228" height="405" class="OB-full" /></p> <p>This routine is better in theory than in practice. The idea is to have a quick succession of LEDs light up in a spiral pattern. This would work a bit better with more LED strands wrapped around the dress.</p> <p>In this case, I’m simply looping through the strands and lighting up successive LEDs. But the nice thing about this routine is that it has a dramatic crescendo to the top, at which point the hula hoop routine begins and it looks like the action started at the bottom and quickly worked its way up to the top, only to smoothly fall back down. It’s a mesmerizing effect.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">void</span> <span class="nf">spiral</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">color</span><span class="p">,</span> <span class="n">byte</span> <span class="n">wait</span><span class="p">)</span> <span class="p">{</span> <span class="k">const</span> <span class="n">byte</span> <span class="n">weight</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span> <span class="n">byte</span> <span class="n">red</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0xFF0000</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">;</span> <span class="n">byte</span> <span class="n">green</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0x00FF00</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">;</span> <span class="n">byte</span> <span class="n">blue</span> <span class="o">=</span> <span class="p">(</span><span class="n">color</span> <span class="o">&amp;</span> <span class="mh">0x0000FF</span><span class="p">);</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">level</span><span class="o">=</span><span class="n">LED_COUNT</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="n">level</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">level</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">strip</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">strip</span> <span class="o">&lt;</span> <span class="n">STRIP_COUNT</span><span class="p">;</span> <span class="n">strip</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="n">clearLEDs</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="k">switch</span> <span class="p">(</span><span class="n">strip</span><span class="p">)</span> <span class="p">{</span> <span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">1</span><span class="p">:</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">2</span><span class="p">:</span> <span class="n">led_b</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">3</span><span class="p">:</span> <span class="n">led_c</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">4</span><span class="p">:</span> <span class="n">led_d</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="k">case</span> <span class="mi">5</span><span class="p">:</span> <span class="n">led_e</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="n">led_f</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">color</span><span class="p">);</span> <span class="n">led_a</span><span class="p">.</span><span class="n">setPixelColor</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">red</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">green</span><span class="o">/</span><span class="n">weight</span><span class="p">,</span> <span class="n">blue</span><span class="o">/</span><span class="n">weight</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="n">led_a</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_b</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_c</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_d</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_e</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">led_f</span><span class="p">.</span><span class="n">show</span><span class="p">();</span> <span class="n">delay</span><span class="p">(</span><span class="n">wait</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <h2 id="random-colors">Random Colors</h2> <p>Since randomness is the spice that gives this dress its mesmerizing qualities, it’s important to have a good list of colors to use.</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">COLORS</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">NAVY</span><span class="p">,</span> <span class="n">DARKBLUE</span><span class="p">,</span> <span class="n">MEDIUMBLUE</span><span class="p">,</span> <span class="n">BLUE</span><span class="p">,</span> <span class="n">DARKGREEN</span><span class="p">,</span> <span class="n">GREEN</span><span class="p">,</span> <span class="n">TEAL</span><span class="p">,</span> <span class="n">DARKCYAN</span><span class="p">,</span> <span class="n">DEEPSKYBLUE</span><span class="p">,</span> <span class="n">DARKTURQUOISE</span><span class="p">,</span> <span class="n">MEDIUMSPRINGGREEN</span><span class="p">,</span> <span class="n">LIME</span><span class="p">,</span> <span class="n">SPRINGGREEN</span><span class="p">,</span> <span class="n">AQUA</span><span class="p">,</span> <span class="n">CYAN</span><span class="p">,</span> <span class="n">MIDNIGHTBLUE</span><span class="p">,</span> <span class="n">DODGERBLUE</span><span class="p">,</span> <span class="n">LIGHTSEAGREEN</span><span class="p">,</span> <span class="n">FORESTGREEN</span><span class="p">,</span> <span class="n">SEAGREEN</span><span class="p">,</span> <span class="n">DARKSLATEGRAY</span><span class="p">,</span> <span class="n">LIMEGREEN</span><span class="p">,</span> <span class="n">MEDIUMSEAGREEN</span><span class="p">,</span> <span class="n">TURQUOISE</span><span class="p">,</span> <span class="n">ROYALBLUE</span><span class="p">,</span> <span class="n">STEELBLUE</span><span class="p">,</span> <span class="n">DARKSLATEBLUE</span><span class="p">,</span> <span class="n">MEDIUMTURQUOISE</span><span class="p">,</span> <span class="n">INDIGO</span><span class="p">,</span> <span class="n">DARKOLIVEGREEN</span><span class="p">,</span> <span class="n">CADETBLUE</span><span class="p">,</span> <span class="n">CORNFLOWERBLUE</span><span class="p">,</span> <span class="n">MEDIUMAQUAMARINE</span><span class="p">,</span> <span class="n">DIMGRAY</span><span class="p">,</span> <span class="n">SLATEBLUE</span><span class="p">,</span> <span class="n">OLIVEDRAB</span><span class="p">,</span> <span class="n">SLATEGRAY</span><span class="p">,</span> <span class="n">LIGHTSLATEGRAY</span><span class="p">,</span> <span class="n">MEDIUMSLATEBLUE</span><span class="p">,</span> <span class="n">LAWNGREEN</span><span class="p">,</span> <span class="n">CHARTREUSE</span><span class="p">,</span> <span class="n">AQUAMARINE</span><span class="p">,</span> <span class="n">MAROON</span><span class="p">,</span> <span class="n">PURPLE</span><span class="p">,</span> <span class="n">OLIVE</span><span class="p">,</span> <span class="n">GRAY</span><span class="p">,</span> <span class="n">SKYBLUE</span><span class="p">,</span> <span class="n">LIGHTSKYBLUE</span><span class="p">,</span> <span class="n">BLUEVIOLET</span><span class="p">,</span> <span class="n">DARKRED</span><span class="p">,</span> <span class="n">DARKMAGENTA</span><span class="p">,</span> <span class="n">SADDLEBROWN</span><span class="p">,</span> <span class="n">DARKSEAGREEN</span><span class="p">,</span> <span class="n">LIGHTGREEN</span><span class="p">,</span> <span class="n">MEDIUMPURPLE</span><span class="p">,</span> <span class="n">DARKVIOLET</span><span class="p">,</span> <span class="n">PALEGREEN</span><span class="p">,</span> <span class="n">DARKORCHID</span><span class="p">,</span> <span class="n">YELLOWGREEN</span><span class="p">,</span> <span class="n">SIENNA</span><span class="p">,</span> <span class="n">BROWN</span><span class="p">,</span> <span class="n">DARKGRAY</span><span class="p">,</span> <span class="n">LIGHTBLUE</span><span class="p">,</span> <span class="n">GREENYELLOW</span><span class="p">,</span> <span class="n">PALETURQUOISE</span><span class="p">,</span> <span class="n">LIGHTSTEELBLUE</span><span class="p">,</span> <span class="n">POWDERBLUE</span><span class="p">,</span> <span class="n">FIREBRICK</span><span class="p">,</span> <span class="n">DARKGOLDENROD</span><span class="p">,</span> <span class="n">MEDIUMORCHID</span><span class="p">,</span> <span class="n">ROSYBROWN</span><span class="p">,</span> <span class="n">DARKKHAKI</span><span class="p">,</span> <span class="n">SILVER</span><span class="p">,</span> <span class="n">MEDIUMVIOLETRED</span><span class="p">,</span> <span class="n">INDIANRED</span><span class="p">,</span> <span class="n">PERU</span><span class="p">,</span> <span class="n">CHOCOLATE</span><span class="p">,</span> <span class="n">TAN</span><span class="p">,</span> <span class="n">LIGHTGRAY</span><span class="p">,</span> <span class="n">THISTLE</span><span class="p">,</span> <span class="n">ORCHID</span><span class="p">,</span> <span class="n">GOLDENROD</span><span class="p">,</span> <span class="n">PALEVIOLETRED</span><span class="p">,</span> <span class="n">CRIMSON</span><span class="p">,</span> <span class="n">GAINSBORO</span><span class="p">,</span> <span class="n">PLUM</span><span class="p">,</span> <span class="n">BURLYWOOD</span><span class="p">,</span> <span class="n">LIGHTCYAN</span><span class="p">,</span> <span class="n">LAVENDER</span><span class="p">,</span> <span class="n">DARKSALMON</span><span class="p">,</span> <span class="n">VIOLET</span><span class="p">,</span> <span class="n">PALEGOLDENROD</span><span class="p">,</span> <span class="n">LIGHTCORAL</span><span class="p">,</span> <span class="n">KHAKI</span><span class="p">,</span> <span class="n">ALICEBLUE</span><span class="p">,</span> <span class="n">HONEYDEW</span><span class="p">,</span> <span class="n">AZURE</span><span class="p">,</span> <span class="n">SANDYBROWN</span><span class="p">,</span> <span class="n">WHEAT</span><span class="p">,</span> <span class="n">BEIGE</span><span class="p">,</span> <span class="n">WHITESMOKE</span><span class="p">,</span> <span class="n">MINTCREAM</span><span class="p">,</span> <span class="n">GHOSTWHITE</span><span class="p">,</span> <span class="n">SALMON</span><span class="p">,</span> <span class="n">ANTIQUEWHITE</span><span class="p">,</span> <span class="n">LINEN</span><span class="p">,</span> <span class="n">LIGHTGOLDENRODYELLOW</span><span class="p">,</span> <span class="n">OLDLACE</span><span class="p">,</span> <span class="n">RED</span><span class="p">,</span> <span class="n">FUCHSIA</span><span class="p">,</span> <span class="n">MAGENTA</span><span class="p">,</span> <span class="n">DEEPPINK</span><span class="p">,</span> <span class="n">ORANGERED</span><span class="p">,</span> <span class="n">TOMATO</span><span class="p">,</span> <span class="n">HOTPINK</span><span class="p">,</span> <span class="n">CORAL</span><span class="p">,</span> <span class="n">DARKORANGE</span><span class="p">,</span> <span class="n">LIGHTSALMON</span><span class="p">,</span> <span class="n">ORANGE</span><span class="p">,</span> <span class="n">LIGHTPINK</span><span class="p">,</span> <span class="n">PINK</span><span class="p">,</span> <span class="n">GOLD</span><span class="p">,</span> <span class="n">PEACHPUFF</span><span class="p">,</span> <span class="n">NAVAJOWHITE</span><span class="p">,</span> <span class="n">MOCCASIN</span><span class="p">,</span> <span class="n">BISQUE</span><span class="p">,</span> <span class="n">MISTYROSE</span><span class="p">,</span> <span class="n">BLANCHEDALMOND</span><span class="p">,</span> <span class="n">PAPAYAWHIP</span><span class="p">,</span> <span class="n">LAVENDERBLUSH</span><span class="p">,</span> <span class="n">SEASHELL</span><span class="p">,</span> <span class="n">CORNSILK</span><span class="p">,</span> <span class="n">LEMONCHIFFON</span><span class="p">,</span> <span class="n">FLORALWHITE</span><span class="p">,</span> <span class="n">SNOW</span><span class="p">,</span> <span class="n">YELLOW</span><span class="p">,</span> <span class="n">LIGHTYELLOW</span><span class="p">,</span> <span class="n">IVORY</span> <span class="p">};</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">randomColor</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">COLORS</span><span class="p">[</span><span class="n">random</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">COLORS</span><span class="p">)</span><span class="o">/</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">))];</span> <span class="p">}</span></code></pre></figure> <h2 id="dancing-to-2014">Dancing to 2014</h2> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/Blue%20EL%20Jacket.jpg" height="405" width="279" class="OB-full" /></p> <p>And what good is a beautiful light-up dress without a well lit dance partner? Here I am decked out in blue EL wire. I spent the better part of three hours sewing this wire into the edges of one of my jackets. But while hand-sewing can take half a day, the result is a great fit and a capable match for Brittany’s dress.</p> <p>The <a href="https://gist.github.com/samuelclay/8276775">full source code for this dress</a> is available on GitHub.</p>Samuel ClayFor New Year’s Eve 2014, my girlfriend and I went to a dance party where wearable electronics were not only encouraged but also on display from a variety of hobbyists. I decided to use this as an opportunity to combine two of my favorite hobbies: sewing and electronics. It is my goal to encourage more people to weave wearable electronics into their own clothing. It’s the future and we might as well look the part. Plus it’s easy to get started and to modify existing code. The full source code for this dress is available on GitHub. Hardware I attached six addressable LED strands from Sparkfun ($20 each) to the lining of Brittany’s dress, and then used a Flora module from Adafruit ($25) to control them. I then used a 3 x AAA battery holder from Adafruit ($2). Setup I used Adafruit’s NeoPixel library to control the LEDs. There were 60 LEDs per 1 meter-long strand. We only needed 40 of the LEDs, but instead of cutting them off, we simply sewed the unused third underneath the strand and cut the software off at 40 LEDs. This way we can repurpose the LED strands when we decide to move them to a new dress. In order to make the connections between the LED strands and the Flora module, I used 30 AWG wire, which is an extremely thin and light wire. The gauge is 0.01” and is as fine as thread. This allowed me to sew the wire into the fabric. I could have used conductive thread, but this wire wrap has a sheath that prevents it from shorting other wires when they touch. It’s also extremely light-weight, so having 18 wires (3 wires per LED strand: power, ground, data) looping around the dress wasn’t an issue. I also want to mention that the code below is hard-coded for six stands. There is a fine line between a hack and a project, and for this, due to my limited time budget, was closer to hack than reusable project. You can easily abstract the code below to account for more or fewer strands, but I was able to ship before midnight on NYE, so I’m considering it a success. void setup() { // To ensure that the first random color in `loop` is never the same randomSeed(analogRead(0)); led_a.begin(); led_b.begin(); led_c.begin(); led_d.begin(); led_e.begin(); led_f.begin(); clearLEDs(true); } void loop() { for (int i=0; i&lt;2; i++) { hulahoop(randomColor(), random(20,60)); } sparkle(randomColor(), random(30,70)); raindrops(randomColor(), random(20,60)); spiral(randomColor(), random(15,30)); } Above we have the code for setup and loop, the two main Arduino routines. Notice that I am repeating the hula hoop routine, since it’s pretty quick and looks good on repeat. I also want to note that every single routine gets its own random color and random delay. This bit of randomness is something I weave into all of my wearable electronics, since it goes just a bit further than off-the-shelf components and shows that there was some intelligence behind the routine. By giving a random delay to each routine I am actually changing the speed of the routine. Sometimes the raindrops would fall quickly, sometimes the hula hoop would have a slow fall and rise. It’s all part of making mesmerizing patterns.Building a living photo frame with a Raspberry Pi and a motion detector2014-01-02T06:00:00+00:002014-01-02T06:00:00+00:00https://ofbrooklyn.com/2014/01/2/building-photo-frame-raspberry-pi-motion-detector<style> .rpi-lcd { margin: 12px 0; } .rpi-lcd img { width: 202px; height: 156px; margin-right: 16px; border: none; } .rpi-image-float { float: right; margin: 0 0 0 24px; } </style> <p>Every hardware hacker has a start, and this one is mine. <a href="http://twitter.com/brittanymorgan">My girlfriend</a> bought me a <a href="http://raspberrypi.org">Raspberry Pi</a> for my birthday, and so I became determined to build something with it for her birthday two months later.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%20Photo%20Frame%20corrected.jpg" style="border: 1px solid #303030; margin: 0 0 24px;" /></p> <p>As you can see above, I built a photo frame that has a few interesting parts. For one, the software which runs the photo frame, which I explore below, keeps the photos fresh from Instagram and Flickr. It then displays a random photo for a configurable six seconds. Secondly, there is a motion detector, built using a PIR sensor, which only turns the monitor on when somebody walks by.</p> <p>This photo frame is easy to build, but it does take a bit of know-how. Mainly, you should feel comfortable soldering wires and mounting the screen and Raspberry Pi to a board or other object. The hard part for me was figuring out how to turn the monitor on and off through the command line.</p> <p>Everything else is gravy, from configuring wifi and autoboot/auto-login on the device to attaching and setting up the motion detecting PIR sensor. You can also use the <a href="http://elinux.org/R-Pi_Hub">eLinux guide</a> to Raspberry Pi and its handy <a href="http://elinux.org/RPi_Hardware_Basic_Setup">RPi Hardware Basic Setup wiki</a>.</p> <h2 id="parts">Parts</h2> <h3 id="raspberry-pi">Raspberry Pi</h3> <p>I chose to use a Raspberry Pi for its simple wifi integration so that photos could be automatically updated. I didn’t want to have to load photos on to an SD card which could then be read by an Arduino.</p> <p>Connecting the monitor was also trivial on a Raspberry Pi, where an Arduino, Maple, or Beagle Bone would require sourcing a connection between the monitor’s composite input and an output from the device.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%20Raspberry%20Pi.jpg" /></p> <h6 id="raspberry-pi-29-on-adafruit"><a href="http://www.adafruit.com/products/1344">Raspberry Pi</a>, $29 on Adafruit.</h6> <p>Make note of the fact that you actually don’t see any of my connections on the top of the board (pictured below). In the below photo, where the Raspberry Pi is flipped vertically to show off the electrical connections, the monitor’s composite cable and the motion detecting PIR sensor’s red wires are soldered underneath.</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/blog/Raspberry%20Pi%20Backside.jpg" /></p> <p>This way the photo frame looks cleaner. If I had connected the monitor using the yellow composite cable, it would have to be with a male-to-male composite adapter, since both the Raspberry Pi and the monitor have a male RCA connection. This would jut out about 2 inches below the device, resulting in a messy look for the frame.</p> <h3 id="35-lcd-monitor">3.5” LCD Monitor</h3> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%203.5%20lcd.jpg" /></p> <h6 id="35-lcd-monitor-45-on-adafruit"><a href="http://www.adafruit.com/products/913">3.5” LCD Monitor</a>, $45 on Adafruit</h6> <p>Note that if you do not plan to solder the composite cable’s two wires, you will need the ugly male-to-male adapter, <a href="http://www.adafruit.com/products/951">sold for $1.50 on Adafruit</a>.</p> <p>There are a number of different sized LCD monitors:</p> <table class="rpi-lcd"> <tr> <td><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%201.5%20lcd.jpg" /></td> <td><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%202%20lcd.jpg" /></td> <td><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%202.5%20lcd.jpg" /></td> </tr> <tr> <td><h6><a href="http://www.adafruit.com/products/910">1.5" LCD</a>, $40</h6></td> <td><h6><a href="http://www.adafruit.com/products/911">2" LCD</a>, $40</h6></td> <td><h6><a href="http://www.adafruit.com/products/912">2.5" LCD</a>, $45</h6></td> </tr> </table> <table class="rpi-lcd"> <tr> <td><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%204.3%20lcd.jpg" /></td> <td><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%207%20lcd.jpg" /></td> <td><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%2010%20lcd.jpg" /></td> </tr> <tr> <td><h6><a href="http://www.adafruit.com/products/946">4.3" LCD</a>, $50</h6></td> <td><h6><a href="http://www.adafruit.com/products/947">7" LCD</a>, $75</h6></td> <td><h6><a href="http://www.adafruit.com/products/1287">10" LCD</a>, $150</h6></td> </tr> </table> <!--more--> <h3 id="4gb-sd-card-with-raspbian-raspberry-pi--debian">4GB SD Card with Raspbian (Raspberry Pi + Debian)</h3> <div class="rpi-image-float"> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%20SD%20Card.jpg" width="200" /> <h6><a href="http://www.adafruit.com/products/1121">4GB SD Card</a>, $10 on Adafruit</h6> </div> <p>This tiny SD card comes pre-loaded with Raspbian. If you prefer to use your own SD card and want to bootload it, just follow <a href="http://elinux.org/RPi_Easy_SD_Card_Setup">eLinux’s Easy SD Card Setup wiki</a>.</p> <div style="clear: both;"></div> <h3 id="miniature-wifi-on-usb">Miniature Wifi on USB</h3> <div class="rpi-image-float"> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%20Wifi.jpg" width="200" /> <h6><a href="http://www.adafruit.com/products/814">USB Wifi</a>, $11 on Adafruit</h6> </div> <p>Unless you’re planning to use an ugly ethernet cable, this tiny wifi USB device works perfectly. It’s easy to setup as well. I followed <a href="http://learn.adafruit.com/adafruits-raspberry-pi-lesson-3-network-setup/overview">Adafruit’s tutorial for setting up wifi on a Raspberry Pi</a>.</p> <div style="clear: both;"></div> <h3 id="passive-infrared-motion-sensor">Passive Infrared Motion Sensor</h3> <div class="rpi-image-float"> <img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%20PIR.jpg" width="200" /> <h6><a href="http://www.adafruit.com/products/189">PIR sensor</a>, $10 on Adafruit</h6> </div> <p>This is a simple and inexpensive component that is responsible for turning the monitor on and off. The accuracy is great, as it never misses a beat, yet I haven’t found it to accidentally trigger in the night unless we walk by. It also has two adjustable dials on the back that allow you to control its sensitivity and delay before firing. <a href="http://www.ladyada.net/learn/sensors/pir.html">Ladyada covers how it works</a>.</p> <div style="clear: both;"></div> <h3 id="soldering-iron">Soldering Iron</h3> <div class="rpi-image-float"> <img src="http://ecx.images-amazon.com/images/I/31NVnBvhZdL._SL500_AA300_.jpg" width="200" /> <h6><a href="http://www.amazon.com/Aoyue-937-Digital-Soldering-Station/dp/B000I30QBW">Aoyue 937+</a>, $60 on Amazon</h6> </div> <p>In order to connect the PIR motion detector and the composite cables, without resorting to the ugly male-to-male adapter, you will need to use a soldering iron. I chose the Aoyue 937+ Digital Soldering Station. Works great and I’m quite happy with it. I should have bought more tips, though.</p> <div style="clear: both;"></div> <h3 id="frame">Frame</h3> <p>This is where you get creative. You effectively just need an enclosure, but I found that picture frames offer the greatest aesthetics-to-enclosure ratio. You just need something that can hold a Raspberry Pi and monitor taped to it. I used double-sided mounting tape as an adhesive, although the wires are being held up by the frame itself.</p> <h2 id="software">Software</h2> <h3 id="raspberry-pi-setup">Raspberry Pi setup</h3> <p>You’ll want to make sure you’re setup with the following:</p> <ul> <li><a href="http://elinux.org/RPi_Debian_Auto_Login">Auto-login</a>, so your Raspberry Pi doesn’t sit at a login prompt.</li> <li><a href="http://learn.adafruit.com/adafruits-raspberry-pi-lesson-3-network-setup/setting-up-wifi-with-occidentalis">Auto-wifi</a>, so you can download images automatically whenever your Raspberry Pi is turned on.</li> <li><a href="http://www.cyberciti.biz/tips/linux-disable-screen-blanking-screen-going-blank.html">Disabled sleep</a>, so your photo frame doesn’t shut off when you don’t want it to.</li> </ul> <h3 id="downloading-personal-photos-from-flickr">Downloading personal photos from Flickr</h3> <p>You’ll need to <a href="http://www.flickr.com/services/apps/create/apply/">register your Flickr API app</a>, which is a quick process. That way you can get a Flickr API key that you can then use to walk the photos from your account. You will also need your <a href="http://idgettr.com">Flickr user id</a>.</p> <p>There are two Python library dependencies for this code:</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell">pip <span class="nb">install </span>flickrapi pip <span class="nb">install </span>requests</code></pre></figure> <p>Once those are installed, save this script as <code class="language-plaintext highlighter-rouge">download_flickr.py</code></p> <figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#!/usr/bin/env python </span> <span class="kn">import</span> <span class="nn">flickrapi</span> <span class="kn">import</span> <span class="nn">requests</span> <span class="n">FLICKR_KEY</span> <span class="o">=</span> <span class="s">"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"</span> <span class="n">USER_ID</span> <span class="o">=</span> <span class="s">"25704617@N04"</span> <span class="k">def</span> <span class="nf">make_url</span><span class="p">(</span><span class="n">photo</span><span class="p">):</span> <span class="c1"># url_template = "http://farm{farm-id}.staticflickr.com/ </span> <span class="c1"># {server-id}/{id}_{secret}_[mstzb].jpg" </span> <span class="n">photo</span><span class="p">[</span><span class="s">'filename'</span><span class="p">]</span> <span class="o">=</span> <span class="s">"%(id)s_%(secret)s_z.jpg"</span> <span class="o">%</span> <span class="n">photo</span> <span class="n">url</span> <span class="o">=</span> <span class="p">(</span><span class="s">"http://farm%(farm)s.staticflickr.com/%(server)s/%(filename)s"</span> <span class="o">%</span> <span class="n">photo</span><span class="p">)</span> <span class="k">return</span> <span class="n">url</span><span class="p">,</span> <span class="n">photo</span><span class="p">[</span><span class="s">'filename'</span><span class="p">]</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span> <span class="k">print</span> <span class="s">" ---&gt; Requesting photos..."</span> <span class="n">flickr</span> <span class="o">=</span> <span class="n">flickrapi</span><span class="p">.</span><span class="n">FlickrAPI</span><span class="p">(</span><span class="n">FLICKR_KEY</span><span class="p">)</span> <span class="n">photos</span> <span class="o">=</span> <span class="n">flickr</span><span class="p">.</span><span class="n">walk</span><span class="p">(</span><span class="n">user_id</span><span class="o">=</span><span class="n">USER_ID</span><span class="p">)</span> <span class="k">for</span> <span class="n">photo</span> <span class="ow">in</span> <span class="n">photos</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="n">filename</span> <span class="o">=</span> <span class="n">make_url</span><span class="p">(</span><span class="n">photo</span><span class="p">.</span><span class="n">__dict__</span><span class="p">[</span><span class="s">'attrib'</span><span class="p">])</span> <span class="n">path</span> <span class="o">=</span> <span class="s">'/home/pi/photoframe/flickr/%s'</span> <span class="o">%</span> <span class="n">filename</span> <span class="k">try</span><span class="p">:</span> <span class="n">image_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">)</span> <span class="k">print</span> <span class="s">" ---&gt; Already have %s"</span> <span class="o">%</span> <span class="n">url</span> <span class="k">except</span> <span class="nb">IOError</span><span class="p">:</span> <span class="k">print</span> <span class="s">" ---&gt; Downloading %s"</span> <span class="o">%</span> <span class="n">url</span> <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span> <span class="n">image_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="n">image_file</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">content</span><span class="p">)</span> <span class="n">image_file</span><span class="p">.</span><span class="n">close</span><span class="p">()</span> <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span> <span class="n">main</span><span class="p">()</span></code></pre></figure> <h3 id="downloading-extra-photos-from-flickr-by-tag-name">Downloading extra photos from Flickr by tag name</h3> <p>Part of the fun of this photo frame is that not only do all of my photos get shown randomly, but I introduced a hodgepodge of random photos by downloading photos from a specific Flickr tag. In this case, I added koala photos, which makes for a pleasant randomness.</p> <p>Save the following command as <code class="language-plaintext highlighter-rouge">download_koalas.sh</code> or whatever you like as a tag.</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">FLICKR_TAG</span><span class="o">=</span>koala <span class="o">&amp;&amp;</span> <span class="se">\</span> wget <span class="s1">'http://api.flickr.com/services/feeds/photos_public.gne?tags=$FLICKR_TAG'</span> <span class="nt">-O-</span> <span class="se">\</span> | <span class="nb">grep</span> <span class="nt">-Po</span> <span class="s1">'http://[^.]+\.staticflickr[^"]+(_b.jpg|_z.jpg)'</span> <span class="se">\</span> | wget <span class="nt">-P</span> /home/pi/photoframe/<span class="nv">$FLICKR_TAG</span> <span class="nt">-nc</span> <span class="nt">-i-</span> </code></pre></figure> <h3 id="automatic-downloading-of-new-photos">Automatic downloading of new photos</h3> <p>In order to have the photos refreshed, you’ll need to have them download in the background. Add these two lines to your crontab with <code class="language-plaintext highlighter-rouge">crontab -e</code> if you’re using both the Flickr photo downloader and the Flickr tag downloader.</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell">0 <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> python /home/pi/photoframe/download_flickr.py 30 <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> /home/pi/photoframe/download_koalas.sh</code></pre></figure> <h3 id="slideshow">Slideshow</h3> <p>Now that we have the photos downloaded and refreshed at a regular interval, we need to get the slideshow running. We’ll use a simple app called the Linux framebuffer imageviewer. Stick this command into a <code class="language-plaintext highlighter-rouge">slideshow.sh</code>.</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell">fbi <span class="nt">-noverbose</span> <span class="nt">-m</span> 640x480 <span class="nt">-a</span> <span class="nt">-u</span> <span class="nt">-t</span> 6 /home/pi/art/<span class="k">**</span>/<span class="k">*</span></code></pre></figure> <p>The option for time is set to 6 seconds, and it uses autozoom to automagically pick a reasonable zoom factor when loading images. The <code class="language-plaintext highlighter-rouge">-u</code> option randomizes the order.</p> <h3 id="detecting-movement">Detecting movement</h3> <p>The slideshow is now running at fullscreen with a randomized assortment of Flickr photos, both your own and from favorite tags. But it’s running even when you’re not there! let’s use the PIR (motion) sensor to turn off the monitor after no movement has been detected for 60 seconds.</p> <p>Save this file as <code class="language-plaintext highlighter-rouge">pir.py</code>.</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#!/usr/bin/env python </span> <span class="kn">import</span> <span class="nn">sys</span> <span class="kn">import</span> <span class="nn">time</span> <span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="n">io</span> <span class="kn">import</span> <span class="nn">subprocess</span> <span class="n">io</span><span class="p">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">io</span><span class="p">.</span><span class="n">BCM</span><span class="p">)</span> <span class="n">SHUTOFF_DELAY</span> <span class="o">=</span> <span class="mi">60</span> <span class="c1"># seconds </span><span class="n">PIR_PIN</span> <span class="o">=</span> <span class="mi">25</span> <span class="c1"># 22 on the board </span><span class="n">LED_PIN</span> <span class="o">=</span> <span class="mi">16</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span> <span class="n">io</span><span class="p">.</span><span class="n">setup</span><span class="p">(</span><span class="n">PIR_PIN</span><span class="p">,</span> <span class="n">io</span><span class="p">.</span><span class="n">IN</span><span class="p">)</span> <span class="n">io</span><span class="p">.</span><span class="n">setup</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">,</span> <span class="n">io</span><span class="p">.</span><span class="n">OUT</span><span class="p">)</span> <span class="n">turned_off</span> <span class="o">=</span> <span class="bp">False</span> <span class="n">last_motion_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="k">while</span> <span class="bp">True</span><span class="p">:</span> <span class="k">if</span> <span class="n">io</span><span class="p">.</span><span class="nb">input</span><span class="p">(</span><span class="n">PIR_PIN</span><span class="p">):</span> <span class="n">last_motion_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="n">io</span><span class="p">.</span><span class="n">output</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">,</span> <span class="n">io</span><span class="p">.</span><span class="n">LOW</span><span class="p">)</span> <span class="k">print</span> <span class="s">"."</span><span class="p">,</span> <span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="n">flush</span><span class="p">()</span> <span class="k">if</span> <span class="n">turned_off</span><span class="p">:</span> <span class="n">turned_off</span> <span class="o">=</span> <span class="bp">False</span> <span class="n">turn_on</span><span class="p">()</span> <span class="k">else</span><span class="p">:</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">turned_off</span> <span class="ow">and</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&gt;</span> <span class="p">(</span><span class="n">last_motion_time</span> <span class="o">+</span> <span class="n">SHUTOFF_DELAY</span><span class="p">):</span> <span class="n">turned_off</span> <span class="o">=</span> <span class="bp">True</span> <span class="n">turn_off</span><span class="p">()</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">turned_off</span> <span class="ow">and</span> <span class="n">time</span><span class="p">.</span><span class="n">time</span><span class="p">()</span> <span class="o">&gt;</span> <span class="p">(</span><span class="n">last_motion_time</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span> <span class="n">io</span><span class="p">.</span><span class="n">output</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">,</span> <span class="n">io</span><span class="p">.</span><span class="n">HIGH</span><span class="p">)</span> <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(.</span><span class="mi">1</span><span class="p">)</span> <span class="k">def</span> <span class="nf">turn_on</span><span class="p">():</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">call</span><span class="p">(</span><span class="s">"sh /home/pi/photoframe/monitor_on.sh"</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> <span class="k">def</span> <span class="nf">turn_off</span><span class="p">():</span> <span class="n">subprocess</span><span class="p">.</span><span class="n">call</span><span class="p">(</span><span class="s">"sh /home/pi/photoframe/monitor_off.sh"</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">main</span><span class="p">()</span> <span class="k">except</span> <span class="nb">KeyboardInterrupt</span><span class="p">:</span> <span class="n">io</span><span class="p">.</span><span class="n">cleanup</span><span class="p">()</span></code></pre></figure> <h3 id="turning-the-monitor-on-and-off">Turning the monitor on and off</h3> <p>There are two ways to turn the monitor on and off. Use the <code class="language-plaintext highlighter-rouge">tvservice</code> command to turn off the monitor port.</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell">pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">chmod </span>0744 monitor_off.sh pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">cat </span>monitor_off.sh tvservice <span class="nt">-o</span> pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">chmod </span>0744 monitor_on.sh pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">cat </span>monitor_on.sh tvservice <span class="nt">-c</span> <span class="s2">"PAL 4:3"</span> <span class="o">&amp;&amp;</span> fbset <span class="nt">-depth</span> 8 <span class="o">&amp;&amp;</span> fbset <span class="nt">-depth</span> 16</code></pre></figure> <p>This method actually turns off the port, which works great except when you’re connected to an HDMI monitor and it shuts off when the port is turned off. When you walk into the room, the port is turned back on but the monitor is off, so it doesn’t come right back up. In this case, simply switch virtual terminals to blank out the screen using the <code class="language-plaintext highlighter-rouge">chvt</code> command.</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell">pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">chmod </span>0744 monitor_off.sh pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">cat </span>monitor_off.sh chvt 2 pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">chmod </span>0744 monitor_on.sh pi@raspberrypi ~/photoframe <span class="nv">$ </span><span class="nb">cat </span>monitor_on.sh chvt 7</code></pre></figure> <h3 id="fixing-the-monitor-edges">Fixing the monitor edges</h3> <p>By default, the image won’t stretch to the edges of your monitor without cajoling.</p> <p>Here’s the before photo:</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/blog/Raspberry%20Pi%20Photo%20Frame.jpg" style="border: 1px solid #303030; margin: 0 0 24px;" /></p> <p>And here’s with margin correction on the monitor:</p> <p><img src="https://s3.amazonaws.com/static.newsblur.com/ofbrooklyn/RPi%20-%20Photo%20Frame%20corrected.jpg" style="border: 1px solid #303030; margin: 0 0 24px;" /></p> <p>To fix this, take a look at using <a href="http://elinux.org/RPiconfig">RPiconfig</a>. All you need to do is edit <code class="language-plaintext highlighter-rouge">/boot/config.txt</code> directly on the Raspberry Pi. The values you need to set are:</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">overscan_left</span><span class="o">=-</span><span class="mi">6</span> <span class="c1"># number of pixels to skip on left </span><span class="n">overscan_right</span><span class="o">=-</span><span class="mi">6</span> <span class="c1"># number of pixels to skip on right </span><span class="n">overscan_top</span><span class="o">=</span><span class="mi">24</span> <span class="c1"># number of pixels to skip on top </span><span class="n">overscan_bottom</span><span class="o">=</span><span class="mi">24</span> <span class="c1"># number of pixels to skip on bottom</span></code></pre></figure> <p>These are my values, but every monitor is different. In order to figure out the values, I would set the values using a binary search (set high then low then halfway between the two and repeat with the new halfway point being the high/low on the correct side), and then rebooting. Eventually I found optimal values.</p> <p>Note that the values will be different from the boot screen to the photo viewer. Obviously, I optimized for the photo viewer, but that means the top and bottom of the boot screen is cut off. Not much better you can expect from a tiny $50 LCD monitor.</p> <p>Also, if you need to rotate or flip the display, it’s easy.</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">display_rotate</span><span class="o">=</span><span class="mi">0</span> <span class="n">Normal</span> <span class="n">display_rotate</span><span class="o">=</span><span class="mi">1</span> <span class="mi">90</span> <span class="n">degrees</span> <span class="n">display_rotate</span><span class="o">=</span><span class="mi">2</span> <span class="mi">180</span> <span class="n">degrees</span> <span class="n">display_rotate</span><span class="o">=</span><span class="mi">3</span> <span class="mi">270</span> <span class="n">degrees</span> <span class="n">display_rotate</span><span class="o">=</span><span class="mh">0x10000</span> <span class="n">horizontal</span> <span class="n">flip</span> <span class="n">display_rotate</span><span class="o">=</span><span class="mh">0x20000</span> <span class="n">vertical</span> <span class="n">flip</span></code></pre></figure> <h3 id="automatic-start-of-the-photo-frame-software">Automatic start of the photo frame software</h3> <p>You’ll want the software to start automatically on boot, so create a new init.d file at <code class="language-plaintext highlighter-rouge">/etc/init.d/flickrd</code>, and add the motion sensor and slideshow scripts to that new file:</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>python /home/pi/photoframe/pir.py /home/pi/photoframe/slideshow.sh</code></pre></figure> <p>Then set the permissions with:</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo chmod </span>755 /etc/init.d/flickrd</code></pre></figure> <p>and finally register the script to be run at startup:</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>update-rc.d flickrd defaults</code></pre></figure> <p>Don’t forget to run the <code class="language-plaintext highlighter-rouge">pir.py</code> script as root, since you’ll need permissions to turn the monitor on and off.</p> <h2 id="troubleshooting">Troubleshooting</h2> <p>Everything above should leave you with a photo slideshow that automatically updates your photos, displays them zoomed in and fullscreen in random order, shutting off the monitor when nobody is around.</p> <p>If you used the code above with no issues, congratulations! You’re in a minority that can work with hardware and get everything right on the first try. For everybody else, we have to troubleshoot. Here are the issues that I ran into.</p> <h3 id="test-your-led">Test your LED</h3> <p>Just getting the Raspberry Pi to respond to my commands was tricky enough, so I wrote this basic program to just blink the onboard LED. This is the Hello, World of the Raspberry Pi.</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="n">GPIO</span> <span class="kn">import</span> <span class="nn">time</span> <span class="n">LED_PIN</span> <span class="o">=</span> <span class="mi">18</span> <span class="k">def</span> <span class="nf">blink</span><span class="p">(</span><span class="n">pin</span><span class="p">):</span> <span class="n">GPIO</span><span class="p">.</span><span class="n">output</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span><span class="n">GPIO</span><span class="p">.</span><span class="n">HIGH</span><span class="p">)</span> <span class="k">print</span> <span class="s">" ---&gt; On"</span> <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(.</span><span class="mi">5</span><span class="p">)</span> <span class="n">GPIO</span><span class="p">.</span><span class="n">output</span><span class="p">(</span><span class="n">pin</span><span class="p">,</span><span class="n">GPIO</span><span class="p">.</span><span class="n">LOW</span><span class="p">)</span> <span class="k">print</span> <span class="s">" ---&gt; Off"</span> <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(.</span><span class="mi">5</span><span class="p">)</span> <span class="k">return</span> <span class="c1"># to use Raspberry Pi board pin numbers </span><span class="n">GPIO</span><span class="p">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="p">.</span><span class="n">BOARD</span><span class="p">)</span> <span class="c1"># set up GPIO output channel </span><span class="n">GPIO</span><span class="p">.</span><span class="n">setup</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="p">.</span><span class="n">OUT</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">3</span><span class="p">):</span> <span class="n">blink</span><span class="p">(</span><span class="n">LED_PIN</span><span class="p">)</span> <span class="n">GPIO</span><span class="p">.</span><span class="n">cleanup</span><span class="p">()</span> </code></pre></figure> <h3 id="sshing-into-your-raspberry-pi">SSH’ing into your Raspberry Pi</h3> <p>You shouldn’t have to rely on an external keyboard and a tiny screen to debug your Raspberry Pi. I suggest using <a href="http://ivanx.com/raspberrypi/">Pi Finder</a> to locate the IP address, if it’s on your local network.</p> <p>You can also use <code class="language-plaintext highlighter-rouge">arp -a</code> to find it.</p> <h3 id="getting-additional-help">Getting additional help</h3> <p>Turn to <a href="http://raspberrypi.stackexchange.com">Stack Exchange’s new Raspberry Pi section</a>. It’s still in beta as of early 2014, but there’s a whole lot of great questions.</p>Samuel ClayEvery hardware hacker has a start, and this one is mine. My girlfriend bought me a Raspberry Pi for my birthday, and so I became determined to build something with it for her birthday two months later. As you can see above, I built a photo frame that has a few interesting parts. For one, the software which runs the photo frame, which I explore below, keeps the photos fresh from Instagram and Flickr. It then displays a random photo for a configurable six seconds. Secondly, there is a motion detector, built using a PIR sensor, which only turns the monitor on when somebody walks by. This photo frame is easy to build, but it does take a bit of know-how. Mainly, you should feel comfortable soldering wires and mounting the screen and Raspberry Pi to a board or other object. The hard part for me was figuring out how to turn the monitor on and off through the command line. Everything else is gravy, from configuring wifi and autoboot/auto-login on the device to attaching and setting up the motion detecting PIR sensor. You can also use the eLinux guide to Raspberry Pi and its handy RPi Hardware Basic Setup wiki. Parts Raspberry Pi I chose to use a Raspberry Pi for its simple wifi integration so that photos could be automatically updated. I didn’t want to have to load photos on to an SD card which could then be read by an Arduino. Connecting the monitor was also trivial on a Raspberry Pi, where an Arduino, Maple, or Beagle Bone would require sourcing a connection between the monitor’s composite input and an output from the device. Raspberry Pi, $29 on Adafruit. Make note of the fact that you actually don’t see any of my connections on the top of the board (pictured below). In the below photo, where the Raspberry Pi is flipped vertically to show off the electrical connections, the monitor’s composite cable and the motion detecting PIR sensor’s red wires are soldered underneath. This way the photo frame looks cleaner. If I had connected the monitor using the yellow composite cable, it would have to be with a male-to-male composite adapter, since both the Raspberry Pi and the monitor have a male RCA connection. This would jut out about 2 inches below the device, resulting in a messy look for the frame. 3.5” LCD Monitor 3.5” LCD Monitor, $45 on Adafruit Note that if you do not plan to solder the composite cable’s two wires, you will need the ugly male-to-male adapter, sold for $1.50 on Adafruit. There are a number of different sized LCD monitors: 1.5" LCD, $40 2" LCD, $40 2.5" LCD, $45 4.3" LCD, $50 7" LCD, $75 10" LCD, $150Backbonification: migrating a large JavaScript project from DOM spaghetti to Backbone.js2012-11-13T18:03:54+00:002012-11-13T18:03:54+00:00https://ofbrooklyn.com/2012/11/13/backbonification-migrating-javascript-to-backbone<p>We’ve all done it. Our code base has one huge monolithic file, packed full of JavaScript spaghetti. It’s unwieldy, hard-to-debug, and has little to no separation of concerns. It is a nightmare to bring new engineers up to speed.</p> <p>This blog post is about decomposing NewsBlur’s single-file 8,500 line JavaScript application into its component parts: 8 models, 12 views, 3 routers, 3 collections. This post explores patterns, techniques, and common pitfalls in migrating from vanilla JavaScript to Backbone.js. It covers moving routers, models, and views, and the process used to migrate a living app.</p> <p><a href="http://www.newsblur.com">NewsBlur</a> is a free RSS feed reader and is <a href="http://github.com/samuelclay">open-source</a>. The benefit of being open-source is that you can see all of the changes I made in this migration by looking through the commit history.</p> <p>As a bit of background, I worked on Backbone.js in its infancy, when Jeremy Ashkenas and I worked on DocumentCloud’s <a href="http://www.documentcloud.org/opensource">many open-source projects</a>.</p> <h2 id="the-presentation">The Presentation</h2> <p>This post was written concurrently with a presentation. Depending on your style, you can either read on or flip through this deck. Both have the same content, but this post expands on every concept in the presentation.</p> <div style="margin: 12px 0 24px;"> <script async="" class="speakerdeck-embed" data-id="4fc7ad0a26bdea017a011a3e" data-ratio="1.3333333333333333" src="//speakerdeck.com/assets/embed.js"></script> </div> <p>There’s no need to go through the presentation. Just read on for the whole kaboodle.</p> <h2 id="pre-reqs-libraries">Pre-reqs: Libraries</h2> <p>There are only two libraries you need to be intimately familiar with in order to make the most of your Backbone transition: Underscore.js and Backbone.js. That means not only being comfortable with reading the source code of these two libraries, but also knowing all of the methods exposed so you can reach into your grab-bag of tricks and pull out the appropriate function.</p> <h3 id="underscorejs">Underscore.js</h3> <p><a href="http://documentcloud.github.com/underscore">Underscore.js</a> is another DocumentCloud library that makes your code more readable and compact by providing useful functions that massage, filter, and jumble data.</p> <p>One popular use of Underscore is creating short pipelines that take a large collection of models and filters it based on conditions. That much is easy. But there are other uses that are beneficial to know.</p> <p>You should be comfortable with all enumerable methods. Think about all of your model collections as reduce-able, filterable, and selectable.</p> <p>Here are two examples of Underscore.js at work:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Get ids of all active feeds</span> <span class="nx">_</span><span class="p">.</span><span class="nx">pluck</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">feeds</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">feed</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">feed</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">);</span> <span class="p">}),</span> <span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// Returns: [42, 36, 72, ...]</span> <span class="c1">// Count fetched/unfetched feeds</span> <span class="kd">var</span> <span class="nx">counts</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">feeds</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">counts</span><span class="p">,</span> <span class="nx">feed</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">feed</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">feed</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">not_yet_fetched</span><span class="dl">'</span><span class="p">)</span> <span class="o">||</span> <span class="nx">feed</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">has_exception</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span> <span class="nx">counts</span><span class="p">[</span><span class="dl">'</span><span class="s1">fetched_feeds</span><span class="dl">'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">counts</span><span class="p">[</span><span class="dl">'</span><span class="s1">unfetched_feeds</span><span class="dl">'</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">counts</span><span class="p">;</span> <span class="p">},</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">unfetched_feeds</span><span class="dl">'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="dl">'</span><span class="s1">fetched_feeds</span><span class="dl">'</span><span class="p">:</span> <span class="mi">0</span> <span class="p">});</span> <span class="c1">// Returns: {'unfetched_feeds': 3, 'fetched_feeds': 42}</span></code></pre></figure> <h3 id="backbonejs">Backbone.js</h3> <p>The star of the show is <a href="http://documentcloud.github.com/backbone">Backbone.js</a>. The entire backbone.js file is fewer than 1,500 lines long, and that’s with 228/1478 lines of whitespace (15%) and 389/1478 lines of comments (26%).</p> <p><img src="http://cl.ly/Ia9O/Screen%20Shot%202012-08-06%20at%20Aug%206%209.22.21%20PM.png" width="500" /></p> <p>This is a basic example of the layout of the four main classes: models, views, collections, and routers. A fifth meta-class called Events is mixed in to each of these classes.</p> <h2 id="how-to-start">How to start</h2> <p>The first step is no easy task. Take your existing design and visually decompose it into its component views. Each view will be represented by either a single model or a combination of models. In fact, you can even have a view not be backed by a model at all.</p> <p>Take the NewsBlur UI for example. It’s a standard three-pane view, with feeds, stories, and story detail:</p> <p><img src="http://f.cl.ly/items/0q3o0b2v1X1n25050s1b/Screen%20Shot%202012-08-06%20at%20Aug%206%209.17.00%20PM.png" /></p> <p>Notice that there are multiple views inside other views. Some views are meant to be simple wrappers around other, more functional views.</p> <!--more--> <p>On the left there is a list of feeds inside a list of folders. These folders and feeds can be embedded inside each other, creating a recursive structure that can be easily assembled by Backbone.js. Each feed view also contains an unread count, a feed title, a favicon, and a feed menu. All of these views are generated by their respective parents. Your job at this stage is to simply figure out what those views are so you can create the appropriate models, views, and controllers (routers).</p> <p>Here’s another example, coming from the DocumentCloud workspace, the original Backbone.js site:</p> <p><img src="http://f.cl.ly/items/1P3N0a07092v3z0k032n/Screen%20Shot%202012-08-06%20at%20Aug%206%209.17.25%20PM.png" /></p> <p>A bit simpler than NewsBlur, this is a classic dual-pane view, with an organizer on the left and a detail pane on the right. Notice that there is a view collection, the document list, that holds numerous document views. It’s important to consider each view as granular as possible and then bring them together in collections that are simply views of model collections.</p> <h2 id="moving-routers">Moving routers</h2> <p>You have routers, even if you don’t realize it yet. Not only that, but you probably have multiple routers. Routers are the smallest part of a Backbone.js project, but are vital because they serve as the entry point for execution.</p> <p><img src="http://f.cl.ly/items/0Q2R2X352K3m2f02453Q/Screen%20Shot%202012-08-06%20at%20Aug%206%209.19.47%20PM.png" width="700" /></p> <p>Anytime a URL is involved, your router should be handling it. You can also have multiple routers in a project. Before version 0.5.0 routers used to be called controllers, if that shines a light on their purpose.</p> <p>If an out-of-band AJAX call is necessary, and it doesn’t correspond to a specific model, then the router is a great place to put it.</p> <p>There’s a lot written on conventions for writing your router. I suggest going directly to the source: <a href="https://github.com/documentcloud/documentcloud/blob/master/public/javascripts/app/workspace.js">the original router from the DocumentCloud workspace</a>. This is the first router ever written and should give you as canonical an idea as possible for what your router should and can include.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Main controller for the journalist workspace. Orchestrates subviews.</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">controllers</span><span class="p">.</span><span class="nx">Workspace</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">Router</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="na">routes</span> <span class="p">:</span> <span class="p">{...},</span> <span class="c1">// Initializes the workspace, binding it to body.</span> <span class="na">initialize</span> <span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">createSubViews</span><span class="p">();</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderSubViews</span><span class="p">();</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">history</span><span class="p">.</span><span class="nx">start</span><span class="p">({</span> <span class="na">pushState</span> <span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">root</span> <span class="p">:</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">account</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">/</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">/public/</span><span class="dl">'</span> <span class="p">})</span> <span class="p">},</span> <span class="c1">// Create all of the requisite subviews.</span> <span class="na">createSubViews</span> <span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">paginator</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">Paginator</span><span class="p">();</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">navigation</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">Navigation</span><span class="p">();</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">toolbar</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">Toolbar</span><span class="p">();</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">organizer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">Organizer</span><span class="p">();</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">searchBox</span> <span class="o">=</span> <span class="nx">VS</span><span class="p">.</span><span class="nx">init</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">searchOptions</span><span class="p">());</span> <span class="k">this</span><span class="p">.</span><span class="nx">sidebar</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">Sidebar</span><span class="p">();</span> <span class="k">this</span><span class="p">.</span><span class="nx">documentList</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">DocumentList</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">dc</span><span class="p">.</span><span class="nx">account</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">uploader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">UploadDialog</span><span class="p">();</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">accounts</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">ui</span><span class="p">.</span><span class="nx">AccountDialog</span><span class="p">();</span> <span class="p">},</span> <span class="c1">// Render all of the existing subviews and place them in the DOM.</span> <span class="na">renderSubViews</span> <span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">content</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#content</span><span class="dl">'</span><span class="p">);</span> <span class="nx">content</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">sidebar</span><span class="p">.</span><span class="nx">render</span><span class="p">().</span><span class="nx">el</span><span class="p">);</span> <span class="nx">content</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">panel</span><span class="p">.</span><span class="nx">render</span><span class="p">().</span><span class="nx">el</span><span class="p">);</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">navigation</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">hotkeys</span><span class="p">.</span><span class="nx">initialize</span><span class="p">();</span> <span class="k">this</span><span class="p">.</span><span class="nx">panel</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">search_box</span><span class="dl">'</span><span class="p">,</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">searchBox</span><span class="p">.</span><span class="nx">render</span><span class="p">().</span><span class="nx">el</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">panel</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">pagination</span><span class="dl">'</span><span class="p">,</span> <span class="nx">dc</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">paginator</span><span class="p">.</span><span class="nx">el</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">panel</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">document_list</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">documentList</span><span class="p">.</span><span class="nx">render</span><span class="p">().</span><span class="nx">el</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">dc</span><span class="p">.</span><span class="nx">account</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">sidebar</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">account_badge</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">accountBadge</span><span class="p">.</span><span class="nx">render</span><span class="p">().</span><span class="nx">el</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span></code></pre></figure> <p>The router is used for laying out all of the workspace-level subviews. Each of these subviews is then responsible for laying out specific instances of documents, collections, toolbar items, search facets, etc.</p> <h2 id="moving-models">Moving models</h2> <p>Before you can even start playing with Backbone models, you’ll need to get your data in a format that Backbone can vivify. Your server should be sending arrays of dictionaries, each array consisting of a single parent model. This may cause versioning on your server end due to having to change the format of your API’s response.</p> <h3 id="versioning-objects-as-dicts-vs-arrays">Versioning: Objects as dicts vs. arrays</h3> <p>Perhaps you were giving data in a format that made it easy for you to key into the dictionary to retrieve a model, like so:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">{</span> <span class="mi">64</span><span class="p">:</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">:</span> <span class="mi">64</span><span class="p">,</span> <span class="dl">'</span><span class="s1">title</span><span class="dl">'</span><span class="p">:</span> <span class="dl">"</span><span class="s2">The NewsBlur Blog</span><span class="dl">"</span> <span class="p">},</span> <span class="mi">128</span><span class="p">:</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>However, in order to vivify these models, Backbone expects an array of dictionaries. We can modify the backend to provide models in this format, by adding a <code class="language-plaintext highlighter-rouge">v=2</code> query paramter:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">[</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">:</span> <span class="mi">64</span><span class="p">,</span> <span class="dl">'</span><span class="s1">title</span><span class="dl">'</span><span class="p">:</span> <span class="dl">"</span><span class="s2">The NewsBlur Blog</span><span class="dl">"</span> <span class="p">},</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> <span class="p">]</span></code></pre></figure> <p>Backbone reads through and finds all <code class="language-plaintext highlighter-rouge">id</code> attributes and hashes them into the _byId object on the model. You can do this client-side instead of versioning your API, but that would require you to write a custom <code class="language-plaintext highlighter-rouge">parse</code> method.</p> <p>You can then override your collections <code class="language-plaintext highlighter-rouge">fetch</code> method to add in the version information. This transparently handles appending a version parameter to your requests.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">fetch</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">options</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">v</span><span class="dl">'</span><span class="p">:</span> <span class="mi">2</span> <span class="p">};</span> <span class="nx">options</span> <span class="o">=</span> <span class="nx">_</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="na">data</span><span class="p">:</span> <span class="nx">data</span><span class="p">,</span> <span class="na">silent</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">options</span><span class="p">);</span> <span class="k">return</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">Collection</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">fetch</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span> <span class="p">},</span></code></pre></figure> <p>Notice, by the way, the last line is how you call <code class="language-plaintext highlighter-rouge">super()</code> in JavaScript. This is a clear demonstration of over-riding methods in Backbone and then calling super at the appropriate time.</p> <h3 id="attributes">Attributes</h3> <p>If your models were simple JavaScript object literals (dictionaries), then you were using one of these styles to work with attributes:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// JavaScript:</span> <span class="nx">model</span><span class="p">.</span><span class="nx">title</span> <span class="nx">model</span><span class="p">[</span><span class="dl">'</span><span class="s1">title</span><span class="dl">'</span><span class="p">]</span> <span class="kd">var</span> <span class="nx">attr</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">title</span><span class="dl">'</span><span class="p">;</span> <span class="nx">model</span><span class="p">[</span><span class="nx">attr</span><span class="p">]</span></code></pre></figure> <p>However, Backbone uses a <code class="language-plaintext highlighter-rouge">get</code> method to retrieve an attribute from a model:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Backbone.js:</span> <span class="nx">model</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">title</span><span class="dl">'</span><span class="p">)</span></code></pre></figure> <p>The trick here is that during a large-scale refactor, you want to change as few things as possible. In this case, you can pass a Backbone model’s <code class="language-plaintext highlighter-rouge">model.attributes</code> to old Javascript methods. Then when done, clean up by looking for <code class="language-plaintext highlighter-rouge">.attributes</code>.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">iconSrc</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">favicon</span><span class="p">(</span><span class="nx">socialFeed</span><span class="p">.</span><span class="nx">attributes</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.NB-header-icon</span><span class="dl">'</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="dl">'</span><span class="s1">src</span><span class="dl">'</span><span class="p">,</span> <span class="nx">iconSrc</span><span class="p">);</span></code></pre></figure> <p>This way you do not have to immediately rewrite all of your model attribute getters until you have tested the modified parts of your code.</p> <h3 id="populating-a-collection-that-has-side-effects">Populating a collection that has side-effects</h3> <p>Looking at feeds and folders above, in order to populate the list of folders you need the feed models. But when populating the feed models, their view is bound to the <code class="language-plaintext highlighter-rouge">reset</code> event, which will try to render the feeds, but there are no folders yet!</p> <p>Pass <code class="language-plaintext highlighter-rouge">{silent: true}</code> to the initial <code class="language-plaintext highlighter-rouge">reset</code> for feeds, then manually trigger the reset event after the dependencies are met.</p> <h3 id="listening-for-events-on-a-collections-models">Listening for events on a collection’s models</h3> <p>Any event that is triggered on a model in a collection will also be triggered on the collection directly, for convenience. This allows you to listen for changes to specific attributes in any model in a collection.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Bind to all models</span> <span class="nx">Documents</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">reset</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">reset</span><span class="p">);</span> <span class="nx">Documents</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">add</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">_addDocument</span><span class="p">);</span> <span class="nx">Documents</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">remove</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">_removeDocument</span><span class="p">);</span> <span class="c1">// Bind to specific attributes on the collection's models</span> <span class="nx">Documents</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">change:pages</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">_renderPageCount</span><span class="p">);</span></code></pre></figure> <p>The <code class="language-plaintext highlighter-rouge">Documents</code> collection contains all of the documents on the page, but you could also create specialized collections with a subset of those models that respond to change events without having to filter the change event on the bigger collection to only apply to those specific models.</p> <h3 id="seeing-changed-attributes">Seeing changed attributes</h3> <p>In this case, a model is updated from elsewhere, so it needs to be refreshed on the page. Use <code class="language-plaintext highlighter-rouge">model.hasChanged()</code> and <code class="language-plaintext highlighter-rouge">model.previousAttributes()</code> to see what’s changed.</p> <p>However, before you may have iterated over all new values and compared to existing values. Backbone has this built in.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// The collection is selective about changing attributes</span> <span class="k">this</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">change</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">_onModelChanged</span><span class="p">);</span> <span class="p">...</span> <span class="nx">_onModelChanged</span> <span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">doc</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">doc</span><span class="p">.</span><span class="nx">hasChanged</span><span class="p">(</span><span class="dl">'</span><span class="s1">access</span><span class="dl">'</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">isPending</span><span class="p">())</span> <span class="k">this</span><span class="p">.</span><span class="nx">_checkForPending</span><span class="p">();</span> <span class="p">},</span> <span class="p">...</span> <span class="c1">// The view is also selective about changing attributes.</span> <span class="c1">// Re-renders the tile if an server-backed attribute changes.</span> <span class="nx">_onDocumentChange</span> <span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">hasChanged</span><span class="p">(</span><span class="dl">'</span><span class="s1">selected</span><span class="dl">'</span><span class="p">))</span> <span class="k">return</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span> <span class="p">},</span></code></pre></figure> <p>You can short-circuit the <code class="language-plaintext highlighter-rouge">change</code> event if you are looking for a specific attribute. But sometimes you want the change event fired only once yet you’re looking to do different things based on which attribute has been changed. So instead of relying on each attribute’s individual change event, you can wait for the bundled change event.</p> <p>Naturally, you may be wondering what gets fired first? Each individual change event or the bundled change event? Well, to figure that out we just turn to the Backbone.js source code:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Call this method to manually fire a `"change"` event for this model and</span> <span class="c1">// a `"change:attribute"` event for each changed attribute.</span> <span class="c1">// Calling this will cause all objects observing the model to update.</span> <span class="nx">change</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">options</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="nx">l</span><span class="o">=</span><span class="nx">triggers</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">l</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">trigger</span><span class="p">(</span><span class="dl">'</span><span class="s1">change:</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">triggers</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="k">this</span><span class="p">,</span> <span class="nx">current</span><span class="p">[</span><span class="nx">triggers</span><span class="p">[</span><span class="nx">i</span><span class="p">]],</span> <span class="nx">options</span><span class="p">);</span> <span class="p">}</span> <span class="p">...</span> <span class="k">this</span><span class="p">.</span><span class="nx">trigger</span><span class="p">(</span><span class="dl">'</span><span class="s1">change</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span> <span class="p">...</span> <span class="p">},</span></code></pre></figure> <p>Notice that the individual change attributes are guaranteed to fire before the bundled change event. This goes to show that the source code for Backbone.js is not as thorny and cumbersome to read as other libraries may have led you to believe about all libraries. <a href="https://raw.github.com/documentcloud/backbone/master/backbone.js">Backbone.js’s source code</a> is easy to follow and is written as close to plain english as possible</p> <h3 id="intermediary-models">Intermediary models</h3> <p>Sometimes an active item needs a bit more meta-data than the non-active counterpart. Take the feed list, for instance. When a feed becomes selected, it needs to be referred to by many other components, each of which needs to know about the active feed. Storing a reference to this model, say ActiveFeed, then allows you to add view-specific meta-data that would be helpful in other views.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Models</span><span class="p">.</span><span class="nx">Feed</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">setSelected</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">feeds</span><span class="p">.</span><span class="nx">deselect</span><span class="p">();</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">activeFeed</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="p">}</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">FeedList</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">findSelected</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">_</span><span class="p">.</span><span class="nx">pluck</span><span class="p">(</span><span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">app</span><span class="p">.</span><span class="nx">activeFeed</span><span class="p">.</span><span class="nx">views</span><span class="p">,</span> <span class="dl">'</span><span class="s1">$el</span><span class="dl">'</span><span class="p">);</span> <span class="p">}</span></code></pre></figure> <p>If you are operating in a loop, then you’ll definitely want to cache a reference to a model like this.</p> <h2 id="moving-views">Moving views</h2> <p>This part of the process is a bit more involved than moving models or simply constructing routers. This is where most of the cleanup is involved.</p> <h3 id="writing-templates">Writing Templates</h3> <p>The first thing that needs to be changed is how DOM fragments are constructed. In NewsBlur’s case, we’re moving from JavaScript element creation to interpolated templates.</p> <h4 id="old-style-manual-dom-element-creation">Old style: Manual DOM element creation</h4> <p>This style is simply a wrapper around a variety of <code class="language-plaintext highlighter-rouge">document.createElement</code> calls, where <code class="language-plaintext highlighter-rouge">$.make</code> will take attributes and add them correctly to the newly created element, as well as appending each of the children in a easy-to-read function.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">openSocialCommentReplyForm</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$comment</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">profile</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">userProfile</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">$form</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">make</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">className</span><span class="p">:</span> <span class="dl">'</span><span class="s1">...</span><span class="dl">'</span> <span class="p">},</span> <span class="p">[</span> <span class="nx">$</span><span class="p">.</span><span class="nx">make</span><span class="p">(</span><span class="dl">'</span><span class="s1">img</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">className</span><span class="p">:</span> <span class="dl">'</span><span class="s1">...</span><span class="dl">'</span><span class="p">,</span> <span class="na">src</span><span class="p">:</span> <span class="nx">profile</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">url</span><span class="dl">'</span><span class="p">)</span> <span class="p">}),</span> <span class="nx">$</span><span class="p">.</span><span class="nx">make</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">className</span><span class="p">:</span> <span class="dl">'</span><span class="s1">...</span><span class="dl">'</span> <span class="p">},</span> <span class="nx">profile</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">username</span><span class="dl">'</span><span class="p">)),</span> <span class="nx">$</span><span class="p">.</span><span class="nx">make</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">className</span><span class="p">:</span> <span class="dl">'</span><span class="s1">...</span><span class="dl">'</span><span class="p">,</span> <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">text</span><span class="dl">'</span> <span class="p">}),</span> <span class="nx">$</span><span class="p">.</span><span class="nx">make</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">className</span><span class="p">:</span> <span class="dl">'</span><span class="s1">...</span><span class="dl">'</span> <span class="p">},</span> <span class="dl">'</span><span class="s1">Post</span><span class="dl">'</span><span class="p">)</span> <span class="p">]);</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.story-comment-reply-form</span><span class="dl">'</span><span class="p">,</span> <span class="nx">$comment</span><span class="p">).</span><span class="nx">remove</span><span class="p">();</span> <span class="nx">$comment</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="nx">$form</span><span class="p">);</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.comments</span><span class="dl">'</span><span class="p">,</span> <span class="nx">$form</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">keydown</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">enter, return</span><span class="dl">'</span><span class="p">,</span> <span class="nx">_</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">saveSocialCommentReply</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="nx">$comment</span><span class="p">));</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.comments</span><span class="dl">'</span><span class="p">,</span> <span class="nx">$form</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">keydown</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">esc</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.NB-story-comment-reply-form</span><span class="dl">'</span><span class="p">,</span> <span class="nx">$comment</span><span class="p">).</span><span class="nx">remove</span><span class="p">();</span> <span class="p">});</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">,</span> <span class="nx">$form</span><span class="p">).</span><span class="nx">focus</span><span class="p">();</span> <span class="k">this</span><span class="p">.</span><span class="nx">fetchStoryLocationsInFeedView</span><span class="p">();</span> <span class="p">},</span></code></pre></figure> <p>While it’s easy to read and write, it is not fast. This method is an order of magnitude slower than the better methods described below, each of which use string interpolation to inject data into the template.</p> <h4 id="template-option-1-inline-strings">Template option #1: inline strings</h4> <p>This is the option that I eventually chose, if only because it was the simplest, could be easily cached by the browser, and was inline with the view code.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">render</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">$feed</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">_</span><span class="p">.</span><span class="nx">template</span><span class="p">(</span><span class="dl">'</span><span class="se">\</span><span class="s1"> &lt;&lt;%= listType %&gt; class="feed"&gt;</span><span class="se">\</span><span class="s1"> &lt;img class="feed-favicon" src="&lt;%= $.favicon(feed) %&gt;"&gt;</span><span class="se">\</span><span class="s1"> &lt;span class="feed-title"&gt;</span><span class="se">\</span><span class="s1"> &lt;%= feed.get("feed_title") %&gt;</span><span class="se">\</span><span class="s1"> &lt;/span&gt;</span><span class="se">\</span><span class="s1"> &lt;div class="feed-exception-icon"&gt;&lt;/div&gt;</span><span class="se">\</span><span class="s1"> &lt;div class="feed-manage-icon"&gt;&lt;/div&gt;</span><span class="se">\</span><span class="s1"> &lt;/&lt;%= listType %&gt;&gt;</span><span class="se">\</span><span class="s1"> </span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">feed</span> <span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">,</span> <span class="na">listType</span> <span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">type</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">feed</span><span class="dl">'</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">li</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">,</span> <span class="p">}));</span> <span class="k">this</span><span class="p">.</span><span class="nx">$el</span><span class="p">.</span><span class="nx">replaceWith</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">setElement</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderCounts</span><span class="p">();</span> <span class="k">return</span> <span class="k">this</span><span class="p">;</span> <span class="p">},</span></code></pre></figure> <p>Notice that <code class="language-plaintext highlighter-rouge">this.setElement</code> is used on the new <code class="language-plaintext highlighter-rouge">$feed</code>. The reason for this is because of <code class="language-plaintext highlighter-rouge">listType</code> changing the top-level element depending on the location of the feed. In some cases it’s part of a list, and in other cases it’s a stand-alone feed title. In order to make it semantically correct, different top-level tags are needed, so you can’t just use <code class="language-plaintext highlighter-rouge">this.$el.html()</code>, otherwise there will still be a top-most <code class="language-plaintext highlighter-rouge">div</code> wrapping your view (which can be customized by setting <code class="language-plaintext highlighter-rouge">this.tagName</code>).</p> <p>Also, note that a better way to create these multi-line strings is to use the heredoc (multiline) strings in CoffeeScript. However, the template string still goes inline, which means you do not have to do any asset pre-compiling, which can be more or less difficult depending on your framework.</p> <h4 id="template-option-2-inline-templates">Template option #2: inline templates</h4> <p>These templates are just <code class="language-plaintext highlighter-rouge">&lt;script&gt;</code> tags that go in your HTML templates. The downside to this method is that your JavaScript templates are not cached by the browser and have to be downloaded as part of every page load.</p> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/html"</span> <span class="na">id=</span><span class="s">"feed-template"</span><span class="nt">&gt;</span> <span class="o">&lt;&lt;%=</span> <span class="nx">listType</span> <span class="o">%&gt;</span> <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">feed</span><span class="dl">"</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">img</span> <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">feed-favicon</span><span class="dl">"</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">&lt;%= $.favicon(feed) %&gt;</span><span class="dl">"</span> <span class="o">/&gt;</span> <span class="o">&lt;</span><span class="nx">span</span> <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">feed-title</span><span class="dl">"</span><span class="o">&gt;</span> <span class="o">&lt;%=</span> <span class="nx">feed</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">feed_title</span><span class="dl">"</span><span class="p">)</span> <span class="o">%&gt;</span> <span class="o">&lt;</span><span class="sr">/span</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">div</span> <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">feed-exception-icon</span><span class="dl">"</span><span class="o">&gt;&lt;</span><span class="sr">/div</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">div</span> <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">feed-manage-icon</span><span class="dl">"</span><span class="o">&gt;&lt;</span><span class="sr">/div</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/&lt;%= listType %&gt;</span><span class="err">&gt; </span><span class="nt">&lt;/script&gt;</span></code></pre></figure> <p>…</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">render</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">template</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">template</span> <span class="o">||</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#feed-template</span><span class="dl">'</span><span class="p">).</span><span class="nx">html</span><span class="p">();</span> <span class="kd">var</span> <span class="nx">$feed</span> <span class="o">=</span> <span class="nx">_</span><span class="p">.</span><span class="nx">template</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">template</span><span class="p">,</span> <span class="p">{</span> <span class="na">feed</span> <span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">,</span> <span class="na">listType</span> <span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">type</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">feed</span><span class="dl">'</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">li</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">,</span> <span class="p">});</span> <span class="k">this</span><span class="p">.</span><span class="nx">$el</span><span class="p">.</span><span class="nx">replaceWith</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">setElement</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="k">return</span> <span class="k">this</span><span class="p">;</span> <span class="p">},</span></code></pre></figure> <p>Not recommended, but easy enough to do without a pre-compiler or asset pipeline.</p> <h4 id="template-option-3-jsts">Template option #3: JSTs</h4> <p>This is the recommended method, but it requires you to have a pre-compiler that works in both development and production, referring to your templates individual in development and as a concatenated file in production. Not a big deal to implement, but many asset packagers do not handle JavaScript templates and allow you to wrap them in the appropriate interpolater, such as Underscore.js’ <code class="language-plaintext highlighter-rouge">_.template</code>.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nb">window</span><span class="p">.</span><span class="nx">JSTs</span><span class="p">[</span><span class="dl">'</span><span class="s1">feed</span><span class="dl">'</span><span class="p">]</span> <span class="o">=</span> <span class="nx">_</span><span class="p">.</span><span class="nx">template</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;script type="text/html" id="feed-template"&gt;</span><span class="dl">'</span><span class="o">+</span> <span class="dl">'</span><span class="s1">&lt;&lt;%= listType %&gt; class="feed"&gt;&lt;img class="feed-favicon" </span><span class="dl">'</span><span class="o">+</span> <span class="dl">'</span><span class="s1">src="&lt;%= $.favicon(feed) %&gt;" /&gt;&lt;span class="feed-title"&gt;</span><span class="dl">'</span><span class="o">+</span> <span class="dl">'</span><span class="s1">&lt;%= feed.get("feed_title") %&gt;&lt;/span&gt;</span><span class="dl">'</span><span class="o">+</span> <span class="dl">'</span><span class="s1">&lt;div class="feed-exception-icon"&gt;&lt;/div&gt;</span><span class="dl">'</span><span class="o">+</span> <span class="dl">'</span><span class="s1">&lt;div class="feed-manage-icon"&gt;&lt;/div&gt;</span><span class="dl">'</span><span class="o">+</span> <span class="dl">'</span><span class="s1">&lt;/&lt;%= listType %&gt;&gt;&lt;/script&gt;</span><span class="dl">'</span><span class="p">);</span> <span class="p">...</span> <span class="nx">render</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">$feed</span> <span class="o">=</span> <span class="nx">JSTs</span><span class="p">[</span><span class="dl">'</span><span class="s1">feed</span><span class="dl">'</span><span class="p">]({</span> <span class="na">feed</span> <span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">,</span> <span class="na">listType</span> <span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">type</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">feed</span><span class="dl">'</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">li</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">,</span> <span class="p">});</span> <span class="k">this</span><span class="p">.</span><span class="nx">$el</span><span class="p">.</span><span class="nx">replaceWith</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">setElement</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="k">return</span> <span class="k">this</span><span class="p">;</span> <span class="p">},</span></code></pre></figure> <p>If you are on Ruby on Rails, you can use <a href="http://documentcloud.github.com/jammit">Jammit</a>, another DocumentCloud project, to automatically handle JavaScript templates. Alternatively, <a href="https://github.com/shaunlee/node-jst">node-jst</a> and <a href="https://github.com/sstephenson/sprockets">sprockets</a> are worth a look.</p> <h3 id="event-delegation">Event delegation</h3> <p>The most common view change you’ll make is moving from event binding to event delegation.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// From:</span> <span class="nx">$</span><span class="p">(</span><span class="dl">"</span><span class="s2">.feed</span><span class="dl">"</span><span class="p">,</span> <span class="nx">$feedList</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">click</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="c1">// To:</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">FeedView</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">View</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="p">...</span> <span class="na">events</span><span class="p">:</span> <span class="p">{</span> <span class="dl">"</span><span class="s2">click</span><span class="dl">"</span> <span class="p">:</span> <span class="dl">"</span><span class="s2">open</span><span class="dl">"</span> <span class="p">}</span> <span class="p">...</span> <span class="p">});</span></code></pre></figure> <p>One of the biggest benefits you’ll receive by moving to Backbone.js is going from event binding to event delegation. If you’re not already familiar with what this is, it is simply attaching all events to the top-level view element and then bubbling any events that happen to a child element up to the parent, where it is caught and delegated to the appropriate method.</p> <p>This also means that you won’t have events bound all over the DOM. And when you destroy views, you know where all of the events are bound and will not have as much work to do in order to prevent memory leaks from events bound to missing elements.</p> <h3 id="delegating-the-same-object-from-multiple-views">Delegating the same object from multiple views</h3> <p>Sometimes an object on your page can be better represented by different views attached to the same element.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">FeedView</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">View</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="na">initialize</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">menu</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">FeedMenuView</span><span class="p">({</span><span class="na">el</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">});</span> <span class="p">}</span> <span class="p">});</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">FeedMenuView</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">View</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="p">...</span> <span class="na">events</span><span class="p">:</span> <span class="p">{</span> <span class="dl">"</span><span class="s2">click</span><span class="dl">"</span> <span class="p">:</span> <span class="dl">"</span><span class="s2">open</span><span class="dl">"</span> <span class="p">}</span> <span class="p">...</span> <span class="p">});</span></code></pre></figure> <p>In this case it’s important to remember that both of these views will be listening for bubbling events. That means that it is possible to create a race condition if you do not know the order these views are instantiated. You will want to be careful in that the separated views do not step on each other’s toes.</p> <p>In this instance the feed title view and the feed menu view are attached at the same place but serve completely different purposes. The feed title view is the “parent” of the menu view, even though they exist at the same level in the DOM hierarchy.</p> <h3 id="which-element-to-use">Which element to use</h3> <p>If your top-level element is complicated and you are creating it as part of your <code class="language-plaintext highlighter-rouge">render</code>, then you can use <code class="language-plaintext highlighter-rouge">setElement</code> instead of <code class="language-plaintext highlighter-rouge">$(this.el).html()</code>.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">$feed</span> <span class="o">=</span> <span class="nx">_</span><span class="p">.</span><span class="nx">template</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;li class="feed"&gt; ... &lt;/li&gt;</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="k">this</span><span class="p">.</span><span class="nx">setElement</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="c1">// Would include a surrounding &lt;div&gt;</span> <span class="c1">// $(this.el).html($feed) </span></code></pre></figure> <p>But you can’t just perform a <code class="language-plaintext highlighter-rouge">setElement</code>, because you need to replace the <code class="language-plaintext highlighter-rouge">$el</code> if it is on the page:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">this</span><span class="p">.</span><span class="nx">$el</span><span class="p">.</span><span class="nx">replaceWith</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">setElement</span><span class="p">(</span><span class="nx">$feed</span><span class="p">);</span></code></pre></figure> <p>This is meant for switching between element types. For example, a view that will sometimes go into a list as a list item, but then also be displayed solo, then you will want to control the <code class="language-plaintext highlighter-rouge">tagName</code> or just include it as part of your template and use <code class="language-plaintext highlighter-rouge">setElement</code>.</p> <h3 id="view-collections">View collections</h3> <p>Models should not know about views. So in order to keep track of views, a parent view should encapsulate them and store references.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">findFeedInFeedList</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">feedId</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">$feeds</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.feed</span><span class="dl">'</span><span class="p">,</span> <span class="nx">$feedList</span><span class="p">).</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">dataId</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="nx">dataId</span> <span class="o">==</span> <span class="nx">feedId</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">this</span><span class="p">;</span> <span class="p">}</span> <span class="p">});</span> <span class="k">return</span> <span class="nx">$feeds</span><span class="p">;</span> <span class="p">}</span></code></pre></figure> <p>[turns into a view collection on FeedListView]</p> <h3 id="recursive-subviews">Recursive subviews</h3> <p style="float: left; margin: 0"><img src="http://f.cl.ly/items/2B1d2x1Y2V1p2y2N0c2j/Screen%20Shot%202012-08-06%20at%20Aug%206%209.png" style="float: left;margin: 0 24px 24px 0;width: 250px" /></p> <p>This is a typical case where you want to have a recursive subview that can contain more of itself. In this case, we have feeds and folders, and both can be children of folders. To accomplish this hierarchy, we just have to descend down the chain, rendering each subview and keeping track of each child view.</p> <p style="margin: 0"><br style="clear:both;" /></p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">Folder</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">View</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="na">render</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">depth</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">options</span><span class="p">.</span><span class="nx">depth</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">$feeds</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">collection</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">item</span><span class="p">.</span><span class="nx">isFeed</span><span class="p">())</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">feed</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">Feed</span><span class="p">({</span> <span class="na">model</span><span class="p">:</span> <span class="nx">item</span><span class="p">.</span><span class="nx">feed</span><span class="p">,</span> <span class="na">depth</span><span class="p">:</span> <span class="nx">depth</span> <span class="p">}).</span><span class="nx">render</span><span class="p">();</span> <span class="nx">item</span><span class="p">.</span><span class="nx">feed</span><span class="p">.</span><span class="nx">views</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">feed</span><span class="p">);</span> <span class="k">return</span> <span class="nx">feed</span><span class="p">.</span><span class="nx">el</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">folder</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">Folder</span><span class="p">({</span> <span class="na">model</span><span class="p">:</span> <span class="nx">item</span><span class="p">,</span> <span class="na">collection</span><span class="p">:</span> <span class="nx">item</span><span class="p">.</span><span class="nx">folders</span><span class="p">,</span> <span class="na">depth</span><span class="p">:</span> <span class="nx">depth</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">}).</span><span class="nx">render</span><span class="p">();</span> <span class="nx">item</span><span class="p">.</span><span class="nx">folderViews</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">folder</span><span class="p">);</span> <span class="k">return</span> <span class="nx">folder</span><span class="p">.</span><span class="nx">el</span><span class="p">;</span> <span class="p">}</span> <span class="p">});</span> <span class="kd">var</span> <span class="nx">$folder</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderFolder</span><span class="p">();</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">$folder</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.folder</span><span class="dl">'</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">$feeds</span><span class="p">);</span> <span class="k">return</span> <span class="k">this</span><span class="p">;</span> <span class="p">}</span> <span class="p">});</span></code></pre></figure> <h3 id="traversing-a-view">Traversing a view</h3> <p>When moving from story to story or feed to feed, you want to move in the order of what’s on screen. The order is handled by the collection, but keeping track of the active model is something you have to do manually.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Go to the next feed. Old style:</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">.feed.selected</span><span class="dl">'</span><span class="p">).</span><span class="nx">next</span><span class="p">(</span><span class="dl">'</span><span class="s1">.next</span><span class="dl">'</span><span class="p">)</span> <span class="c1">// New style:</span> <span class="nx">Feeds</span><span class="p">.</span><span class="nx">activeFeed</span> <span class="o">=</span> <span class="nx">Feeds</span><span class="p">.</span><span class="nx">next</span><span class="p">();</span> <span class="nx">Feeds</span><span class="p">.</span><span class="nx">activeFeed</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="dl">'</span><span class="s1">selected</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span></code></pre></figure> <p>The <code class="language-plaintext highlighter-rouge">Feeds.next()</code> method can be a complicated method that walks your recursive hierarchy. But that’s hidden away and you can just call that method idempotently.</p> <h3 id="action-hierarchy">Action hierarchy</h3> <p>Views handle their own actions, but what about cross-view actions? One view modifies another view. Propagate that up to the Router or parent view that is drawing the views. Have the parent talk to the necessary models, changing appropriate data.</p> <p>Once the data is changed, the correct views will update, based on their triggers and bindings.</p> <p>For instance, if you are deleting a feed, the context menu view, which knows which $feed is being deleted, sends that info to the model, which then scans its own views and triggers the removal on the correct one. Finally, an AJAX request is made (this is optimistic) in the Router.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Views</span><span class="p">.</span><span class="nx">FeedMenu</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">View</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="na">deleteFeed</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">removeFeedFromFolder</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">folder</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span> <span class="nx">NEWSBLUR</span><span class="p">.</span><span class="nx">Models</span><span class="p">.</span><span class="nx">Feed</span> <span class="o">=</span> <span class="nx">Backbone</span><span class="p">.</span><span class="nx">Model</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="na">removeFeedFromFolder</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">folder</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">feedViews</span><span class="p">.</span><span class="nx">chain</span><span class="p">().</span><span class="nx">select</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">feedView</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">feedView</span><span class="p">.</span><span class="nx">folder</span> <span class="o">==</span> <span class="nx">folder</span><span class="p">;</span> <span class="p">}).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">feedView</span><span class="p">)</span> <span class="p">{</span> <span class="nx">feedView</span><span class="p">.</span><span class="nx">animateDestroy</span><span class="p">();</span> <span class="p">});</span> <span class="p">}</span> <span class="p">});</span></code></pre></figure> <h3 id="no-need-for-a-model-to-back-a-view">No need for a model to back a view</h3> <p><img src="http://f.cl.ly/items/2Q281m0O0T2g1B1Z1B3L/Screen%20Shot%202012-08-06%20at%20Aug%206%209.21.37%20PM.png" width="700" /></p> <p>Some views just don’t have models. They control a visual element on the page but have no corresponding server model. The closest object they have to a model is the page itself or the user.</p> <p>This means you need to keep a reference to the view. You can’t just rely on the model to update the view. Other times you do have a model. If the model for the view would change depending on the model, you can just render a new view with the correct model and replace it on the page.</p> <h2 id="common-pitfalls">Common pitfalls</h2> <h3 id="typeerror-undefined-is-not-an-object-evaluating-funcbind">TypeError: ‘undefined’ is not an object (evaluating ‘func.bind’)</h3> <p>This error comes from trying to bind to a method that doesn’t exist. But you don’t get the name or line of the error, so the only way to debug it is to set a breakpoint and work your way up the stack.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">_</span><span class="p">.</span><span class="nx">bindAll</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="dl">'</span><span class="s1">render</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">open</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">methodRemovedAndWillThrowTypeError</span><span class="dl">'</span><span class="p">);</span></code></pre></figure> <h3 id="firing-a-change-event-while-still-setting-up-models-and-views">Firing a change event while still setting up models and views</h3> <p>Add <code class="language-plaintext highlighter-rouge">{silent: true}</code> to a <code class="language-plaintext highlighter-rouge">model.set()</code> call if you’re not ready to handle the change events.</p> <h3 id="selectively-re-rendertoggle-classes-based-on-specific-change-events">Selectively re-render/toggle Classes based on specific change events.</h3> <p>Sometimes an attribute change merely results in a changed class on the element, not a full render. One technique you can use is to bind to the bundled <code class="language-plaintext highlighter-rouge">change</code> event and then selectively look for attributes that only result in a class change.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">onChange</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">onlyClasses</span> <span class="o">=</span> <span class="nx">_</span><span class="p">.</span><span class="nx">all</span><span class="p">(</span><span class="nx">_</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">changed</span><span class="p">),</span> <span class="kd">function</span><span class="p">(</span><span class="nx">change</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">_</span><span class="p">.</span><span class="nx">contains</span><span class="p">([</span><span class="dl">'</span><span class="s1">selected</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">has_exception</span><span class="dl">'</span><span class="p">],</span> <span class="nx">change</span><span class="p">);</span> <span class="p">};</span> <span class="k">if</span> <span class="p">(</span><span class="nx">onlyClasses</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">toggleClasses</span><span class="p">();</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="k">this</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span> <span class="p">}</span></code></pre></figure> <p>Here, we’re checking that every changed attribute is one that results in a class change. Otherwise, do the full render.</p> <h3 id="cleanup-of-ghost-views">Cleanup of ghost views</h3> <p>When removing a view, you need to both remove it from the DOM and then unbind it. The model still has bindings to the destroyed view.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Parent view:</span> <span class="nx">view</span><span class="p">.</span><span class="nx">destroy</span><span class="p">();</span> <span class="c1">// View:</span> <span class="nl">initialize</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">change</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">render</span><span class="p">);</span> <span class="p">},</span> <span class="nx">destroy</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span> <span class="k">this</span><span class="p">.</span><span class="nx">model</span><span class="p">.</span><span class="nx">unbind</span><span class="p">(</span><span class="dl">'</span><span class="s1">change</span><span class="dl">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">render</span><span class="p">);</span> <span class="p">},</span></code></pre></figure> <p>This has changed in the latest version of Backbone.js, but it’s not yet released (the coming version 1.0), so you have to manually destroy both the views and view’s event bindings.</p> <h3 id="the-disappearing-view">The disappearing view</h3> <p><img src="http://f.cl.ly/items/0p431V1f3B2r3r2h1W0T/Screen%20Shot%202012-08-06%20at%20Aug%206%209.21.52%20PM.png" style="float: left; margin: 0 16px 0 0; line-height: 0; width: 160px; border: 1px solid #808080;" /></p> <p>Before Backbone, views would not automatically update from beneath you. Now that views are tied to models, check to see if you are modifying a view post-render, just as inserting a special sub-view that the parent view doesn’t know about. Because when the view re-renders, it won’t know to re-insert the subview.</p> <p style="margin: 0"><br style="clear:both;" /></p> <h2 id="resources">Resources</h2> <p>A couple resources that I like are:</p> <ul> <li><a href="http://addyosmani.github.com/backbone-fundamentals">Developing Backbone.js Applications</a> by Addy Osmani</li> <li><a href="http://ricostacruz.com/backbone-patterns">Backbone Patterns</a> by Rico Sta. Cruz</li> </ul> <p>As always, make sure to read the source of <a href="http://github.com/documentcloud/backbone">Backbone.js</a> to see if you can just figure out what’s happening under the hood.</p> <p>I’m <a href="http://github.com/samuelclay">@samuelclay on GitHub</a>, where you can follow me to watch the development of the NewsBlur front-end, back-end, iOS and Android apps. And I’m <a href="http://twitter.com/samuelclay">@samuelclay on Twitter</a> where you can ask me further questions about Backbone.js.</p>Samuel ClayWe’ve all done it. Our code base has one huge monolithic file, packed full of JavaScript spaghetti. It’s unwieldy, hard-to-debug, and has little to no separation of concerns. It is a nightmare to bring new engineers up to speed. This blog post is about decomposing NewsBlur’s single-file 8,500 line JavaScript application into its component parts: 8 models, 12 views, 3 routers, 3 collections. This post explores patterns, techniques, and common pitfalls in migrating from vanilla JavaScript to Backbone.js. It covers moving routers, models, and views, and the process used to migrate a living app. NewsBlur is a free RSS feed reader and is open-source. The benefit of being open-source is that you can see all of the changes I made in this migration by looking through the commit history. As a bit of background, I worked on Backbone.js in its infancy, when Jeremy Ashkenas and I worked on DocumentCloud’s many open-source projects. The Presentation This post was written concurrently with a presentation. Depending on your style, you can either read on or flip through this deck. Both have the same content, but this post expands on every concept in the presentation. There’s no need to go through the presentation. Just read on for the whole kaboodle. Pre-reqs: Libraries There are only two libraries you need to be intimately familiar with in order to make the most of your Backbone transition: Underscore.js and Backbone.js. That means not only being comfortable with reading the source code of these two libraries, but also knowing all of the methods exposed so you can reach into your grab-bag of tricks and pull out the appropriate function. Underscore.js Underscore.js is another DocumentCloud library that makes your code more readable and compact by providing useful functions that massage, filter, and jumble data. One popular use of Underscore is creating short pipelines that take a large collection of models and filters it based on conditions. That much is easy. But there are other uses that are beneficial to know. You should be comfortable with all enumerable methods. Think about all of your model collections as reduce-able, filterable, and selectable. Here are two examples of Underscore.js at work: // Get ids of all active feeds _.pluck(this.feeds.select(function(feed) { return feed.get('active'); }), 'id'); // Returns: [42, 36, 72, ...] // Count fetched/unfetched feeds var counts = this.feeds.reduce(function(counts, feed) { if (feed.get('active')) { if (!feed.get('not_yet_fetched') || feed.get('has_exception')) { counts['fetched_feeds'] += 1; } else { counts['unfetched_feeds'] += 1; } } return counts; }, { 'unfetched_feeds': 0, 'fetched_feeds': 0 }); // Returns: {'unfetched_feeds': 3, 'fetched_feeds': 42} Backbone.js The star of the show is Backbone.js. The entire backbone.js file is fewer than 1,500 lines long, and that’s with 228/1478 lines of whitespace (15%) and 389/1478 lines of comments (26%). This is a basic example of the layout of the four main classes: models, views, collections, and routers. A fifth meta-class called Events is mixed in to each of these classes. How to start The first step is no easy task. Take your existing design and visually decompose it into its component views. Each view will be represented by either a single model or a combination of models. In fact, you can even have a view not be backed by a model at all. Take the NewsBlur UI for example. It’s a standard three-pane view, with feeds, stories, and story detail: Notice that there are multiple views inside other views. Some views are meant to be simple wrappers around other, more functional views.For Good Reason, NewsBlur Will Not Compete With the Big Boys2011-01-10T17:26:08+00:002011-01-10T17:26:08+00:00https://ofbrooklyn.com/2011/01/10/good-reason-newsblur-will-not-compete-big-boys<p>NewsBlur has and, for as far out as I can see, will be a side-project. It’s fun, but I can’t even begin to imagine the headaches I’d face if I had to support a living salary through NewsBlur. My goal is just to meet potential co-founders and to try to make a splash. And I’ve been meeting folks here in NYC who I would not have met otherwise. So it’s working quite well, so far. Fabulously well, in fact.</p> <p>Raising money for something like this must be difficult and a huge crapshoot. There are a lot of fadish readers out there who have already sucked up most of the goodwill that the press is willing to give to this area, and even then, they have a design element that is extremely hard to compete with. Financial backers would much rather shut a site down than let it live a meager existence. They’re not out to support a site, they want a 10x return. Funding doesn’t make any sense for what I want.</p> <p>Besides, now I can build fun features like social, the iPhone app, and river of news in peace. Who knows what markets I would have to chase if I had stakeholders. Enterprise! Aggregators! Ick.</p> <p>I think any other business is a better business to be in, but who knows if my tune will change within the next 12 months, as I roll out bigger features.</p>Samuel ClayNewsBlur has and, for as far out as I can see, will be a side-project. It’s fun, but I can’t even begin to imagine the headaches I’d face if I had to support a living salary through NewsBlur. My goal is just to meet potential co-founders and to try to make a splash. And I’ve been meeting folks here in NYC who I would not have met otherwise. So it’s working quite well, so far. Fabulously well, in fact. Raising money for something like this must be difficult and a huge crapshoot. There are a lot of fadish readers out there who have already sucked up most of the goodwill that the press is willing to give to this area, and even then, they have a design element that is extremely hard to compete with. Financial backers would much rather shut a site down than let it live a meager existence. They’re not out to support a site, they want a 10x return. Funding doesn’t make any sense for what I want. Besides, now I can build fun features like social, the iPhone app, and river of news in peace. Who knows what markets I would have to chase if I had stakeholders. Enterprise! Aggregators! Ick. I think any other business is a better business to be in, but who knows if my tune will change within the next 12 months, as I roll out bigger features.