Continuous Integration with Elixir and NixOS
Declarative configuration & release supervision
\\/p>\\n
On the CI server, we followed the official Vultr NixOS install guide<\\/a>, cloned our CI repo<\\/a>, and rebuilt<\\/a> using the configuration.nix file that came with it. Here\\u2019s a small portion of the configuration showing how we import and configure the system services that power the CI:<\\/p>\\n\"},{\"acf_fc_layout\":\"code\",\"language\":\"nix\",\"code\":\"{ config, pkgs, ... }:\\r\\n\\r\\n{\\r\\n imports = [\\r\\n .\\/elixir-release.nix\\r\\n .\\/fsl-ci.nix\\r\\n .\\/hardware-configuration.nix\\r\\n ];\\r\\n\\r\\n\\u2026\\r\\n services = {\\r\\n elixirRelease = {\\r\\n enable = true;\\r\\n appName = \\\"example_app\\\";\\r\\n serviceUser = \\\"phoenix\\\";\\r\\n };\\r\\n fslCI = {\\r\\n enable = true;\\r\\n appName = \\\"ci\\\";\\r\\n serviceUser = \\\"phoenix\\\";\\r\\n };\\r\\n };\\r\\n}\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" Why systemd? People seem to have a love\\/hate relationship with systemd<\\/a>. It comes with NixOS and we find it to be useful. Similar to Erlang and OTP, systemd\\u2019s design emphasizes process management as a way of achieving stability. It functions at the OS level, providing an interface between system processes and the Linux kernel. OTP on the other hand, deals with much more lightweight BEAM processes orchestrated within the release service process.<\\/p>\\n The CI service is designed to listen for webhook activity from BitBucket<\\/a> triggered by commits to the repo we want to build. Upon receipt, the webhook endpoint employs a plug<\\/a> to validate the remote IP and payload signature against expected values, ensuring authenticity.<\\/p>\\n\"},{\"acf_fc_layout\":\"code\",\"language\":\"elixir\",\"code\":\"defmodule CI.GitWebhookController do\\r\\n require Logger\\r\\n\\r\\n import Plug.Conn\\r\\n\\r\\n def bitbucket(conn, %{\\r\\n \\\"repository\\\" => %{\\\"name\\\" => repository_name},\\r\\n \\\"push\\\" => %{\\r\\n \\\"changes\\\" => [\\r\\n %{\\r\\n \\\"new\\\" => %{\\r\\n \\\"name\\\" => branch_name,\\r\\n \\\"target\\\" => %{\\\"hash\\\" => commitish}\\r\\n }\\r\\n }\\r\\n | _subsequent_changes\\r\\n ]\\r\\n }\\r\\n }) do\\r\\n if validate_bitbucket_ip(conn.remote_ip) do\\r\\n Logger.info(\\\"Bitbucket webhook on #{branch_name} of #{repository_name}\\\")\\r\\n Process.send({ReleaseRunner, node()}, {:run, {repository_name, branch_name, commitish}}, [])\\r\\n send_resp(conn, 200, \\\"webhook received\\\")\\r\\n else\\r\\n send_resp(conn, 403, \\\"access denied\\\")\\r\\n end\\r\\n end\\r\\n \\u2026\\r\\nend\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" Next, the CI service checks out the specific commit that triggered the build, preventing race conditions with multiple committers. If there are changes that warrant a new release, it runs a bash script that performs the necessary steps to create an Elixir release. This is where we strongly considered using sasa1977\\/ci<\\/a>. Since the status of the project was \\u201cstill far from being usable\\u201d according to Sa\\u0161a<\\/a> when we set out on this journey, we decided to take the LTS route by writing our own bash scripts for compiling and cutting releases. We\\u2019re still interested in incorporating Sa\\u0161a\\u2019s library here at some point.<\\/p>\\n An example release script that the CI runs to deploy a new Elixir release:<\\/p>\\n\"},{\"acf_fc_layout\":\"code\",\"language\":\"bash\",\"code\":\"#!\\/usr\\/bin\\/env sh\\r\\n\\r\\nset -e\\r\\n# set -o allexport; source .env; set +o allexport\\r\\n\\r\\nif [ -z \\\"$1\\\" ]\\r\\nthen\\r\\n branch=main\\r\\nelse\\r\\n branch=$1\\r\\nfi\\r\\n\\r\\nif [ -z \\\"$2\\\" ]\\r\\nthen\\r\\n commit=$branch\\r\\nelse\\r\\n commit=$2\\r\\nfi\\r\\n\\r\\necho \\\"$(date +%Y-%m-%d\\\\ -\\\\ %H:%M:%S) Running new release on commit ${commit} of ${branch} branch\\\"\\r\\n\\r\\nif [ -z \\\"$2\\\" ]\\r\\nthen\\r\\n git checkout -f \\\"${branch}\\\"\\r\\n git pull origin \\\"${branch}\\\"\\r\\nelse\\r\\n git pull origin \\\"${branch}\\\"\\r\\n git checkout -f \\\"${commit}\\\"\\r\\nfi\\r\\n\\r\\nsource \\/home\\/phoenix\\/project\\/.envrc\\r\\nyarn --cwd assets install\\r\\nyarn --cwd assets deploy\\r\\nmix deps.get --only prod\\r\\nmix compile\\r\\nmix ecto.migrate\\r\\nmix phx.digest\\r\\nmix release --overwrite --force\\r\\nsudo systemctl restart elixirRelease\\r\\n\\r\\nexit 0\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" Given careful, declarative configuration of environment variables, paths, and user permissions, the CI service user, a non-root user, is able to restart the systemd service that runs the Elixir release binary. At this point, the new release gets loaded from the _build path of the project directory.<\\/p>\\n <\\/p>\\n\"},{\"acf_fc_layout\":\"call_to_action\",\"theme\":\"light\",\"text\":\"Do you have a deployment story about Elixir or an opinionated method you prefer to use? Feel free to reach out to us with your own experience deploying Elixir applications.\",\"has_button\":true,\"button\":{\"text\":\"Get in touch!\",\"link\":\"https:\\/\\/fullsteamlabs.com\\/contact\",\"position\":\"below\"}},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" Although far from fully featured, our proof of concept has managed to provide us with painless automated deployment of Elixir releases. We have more trust in our uptime guarantees thanks to the killer combo of systemd and OTP. And as a knock-on benefit, we get excellent system service logging capabilities through journalctl<\\/a>.<\\/p>\\n Of course, there\\u2019s plenty of room to improve upon what we\\u2019ve done so far. That\\u2019s part of why we\\u2019re sharing this! Among the top improvements we identified were: Slack notifications, remote deployment, support for GitHub or GitLab, and a rollback mechanism. For future use and adoption by others, we\\u2019d like to improve the ergonomics around configuration and document the full setup procedures. We intentionally avoided these details on the initial release, just to prove that it could be done, but would like to add these improvements in the near future.<\\/p>\\n\"},{\"acf_fc_layout\":\"author\",\"author_type\":\"wp_user\",\"user\":{\"ID\":2,\"user_firstname\":\"Charles\",\"user_lastname\":\"Suggs\",\"nickname\":\"charles\",\"user_nicename\":\"charles\",\"display_name\":\"Charles Suggs\",\"user_email\":\"charles@fullsteamlabs.com\",\"user_url\":\"\",\"user_registered\":\"2019-05-20 17:41:59\",\"user_description\":\"is a co-founder and full stack developer at FullSteam Labs. He enjoys building useful tools for environmental sustainability, from firmware and servers through to the user interface.\",\"user_avatar\":\" We keep a medium-sized home garden at home, and having moved north we wanted to extend our growing season using techniques like low tunnels and a small hoop house. Springtime temperatures can fluctuate significantly in the Midwest; you can quickly go from freezing to frying under plastic. An automated warning system has saved us a lot of stress as well as our baby plants.<\\/p>\\n <\\/p>\\n As the RuuviCollector pulls in data from the Ruuvi tags, it forwards that along to InfluxDB<\\/a> on an old laptop in the closet. Grafana<\\/a> visualizes the data stored in InfluxDB, and enables exploring the historical data.<\\/p>\\n\"},{\"acf_fc_layout\":\"image\",\"image\":{\"ID\":788,\"id\":788,\"title\":\"historical-data\",\"filename\":\"historical-data.png\",\"filesize\":86134,\"url\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/historical-data.png\",\"link\":\"https:\\/\\/rocket.fullsteamlabs.com\\/blog\\/environmental-sensors-early-warning-system-at-home\\/historical-data\\/\",\"alt\":\"Graph showing temperature over time from multiple sensors.\",\"author\":\"1\",\"description\":\"\",\"caption\":\"\",\"name\":\"historical-data\",\"status\":\"inherit\",\"uploaded_to\":785,\"date\":\"2022-07-22 16:11:41\",\"modified\":\"2022-07-22 16:11:59\",\"menu_order\":0,\"mime_type\":\"image\\/png\",\"type\":\"image\",\"subtype\":\"png\",\"icon\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-includes\\/images\\/media\\/default.png\",\"width\":732,\"height\":296,\"sizes\":{\"thumbnail\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/historical-data-150x150.png\",\"thumbnail-width\":150,\"thumbnail-height\":150,\"medium\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/historical-data-575x233.png\",\"medium-width\":575,\"medium-height\":233,\"medium_large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/historical-data.png\",\"medium_large-width\":732,\"medium_large-height\":296,\"large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/historical-data.png\",\"large-width\":732,\"large-height\":296,\"1536x1536\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/historical-data.png\",\"1536x1536-width\":732,\"1536x1536-height\":296,\"2048x2048\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/historical-data.png\",\"2048x2048-width\":732,\"2048x2048-height\":296}},\"width\":\"full\",\"caption\":\"\",\"align\":false},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" InfluxDB is an open source temporal database focused on storing time series data, and Grafana is an open source data graphing, dashboard, and alerting web application.<\\/p>\\n Running this Java utility along with Nginx, Grafana, and InfluxDB was a bit too much for the Raspberry Pi, so I gave an old laptop a new life as a closet server. I moved Nginx, Grafana, and InfluxDB to the laptop and configured the RuuviCollector to pass on the data to InfluxDB on the laptop.<\\/p>\\n Grafana provides some alert functionality with several built-in notification channel types. To keep costs at $0 and minimize setup time, Slack was a good option. With the Slack mobile app, Grafana can alert me via smartphone when the low tunnel\\u2019s temperature is getting hot enough to open up the plastic, or when the refrigerator\\u2019s temperature or humidity keeps rising above its setting. In this case we knew something was up because the humidity kept rising inside the refrigerator over three days.<\\/p>\\n\"},{\"acf_fc_layout\":\"image\",\"image\":{\"ID\":789,\"id\":789,\"title\":\"rising-humidity\",\"filename\":\"rising-humidity.png\",\"filesize\":29610,\"url\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/rising-humidity.png\",\"link\":\"https:\\/\\/rocket.fullsteamlabs.com\\/blog\\/environmental-sensors-early-warning-system-at-home\\/rising-humidity\\/\",\"alt\":\"Graph showing rising humidity levels.\",\"author\":\"1\",\"description\":\"\",\"caption\":\"\",\"name\":\"rising-humidity\",\"status\":\"inherit\",\"uploaded_to\":785,\"date\":\"2022-07-22 16:13:07\",\"modified\":\"2022-07-22 16:13:17\",\"menu_order\":0,\"mime_type\":\"image\\/png\",\"type\":\"image\",\"subtype\":\"png\",\"icon\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-includes\\/images\\/media\\/default.png\",\"width\":829,\"height\":296,\"sizes\":{\"thumbnail\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/rising-humidity-150x150.png\",\"thumbnail-width\":150,\"thumbnail-height\":150,\"medium\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/rising-humidity-575x205.png\",\"medium-width\":575,\"medium-height\":205,\"medium_large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/rising-humidity-768x274.png\",\"medium_large-width\":768,\"medium_large-height\":274,\"large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/rising-humidity.png\",\"large-width\":829,\"large-height\":296,\"1536x1536\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/rising-humidity.png\",\"1536x1536-width\":829,\"1536x1536-height\":296,\"2048x2048\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/rising-humidity.png\",\"2048x2048-width\":829,\"2048x2048-height\":296}},\"width\":\"full\",\"caption\":\"Rising humidity in the refrigerator.\",\"align\":false},{\"acf_fc_layout\":\"signup\",\"heading_text\":\"Sign up here to get tools, tips, and insights delivered to your inbox.\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" The final piece was the ability to check on sensor readings while away from home. The easiest option would have been to pay my Internet service provider for a static IP, a small monthly fee, but I would rather keep the project cost at $0. Some DNS providers also expose a REST API for modifying your DNS. An Elixir GenServer<\\/a> in a supervision tree seemed like a logical choice for a small service that would inherently keep itself running, essentially for free. I decided to write a DNS Updater utility that checks for its public IP and, if it\\u2019s different from the last one it saw, makes an update to the DNS rules keeping the Grafana site available to the world.<\\/p>\\n This is the bulk of the business logic that keeps the DNS updated:<\\/p>\\n\"},{\"acf_fc_layout\":\"code\",\"language\":\"elixir\",\"code\":\" @impl true\\r\\n def init(_opts) do\\r\\n new_state =\\r\\n get_ip_from_dns(%State{dns_ip: nil, ip: nil})\\r\\n |> get_ip()\\r\\n\\r\\n Logger.info(\\\"heartbeat @ 1 \\/ #{@check_delay \\/ 1000 \\/ 60} minutes\\\")\\r\\n :timer.send_interval(@check_delay, :run)\\r\\n {:ok, new_state}\\r\\n end\\r\\n\\r\\n @spec ensure_dns_updated(State) :: State\\r\\n def ensure_dns_updated(%State{\\r\\n :ip => ip,\\r\\n :dns_ip => dns_ip} = state)\\r\\n when ip != dns_ip do\\r\\n Logger.info(\\\"different ip\\\")\\r\\n case update_dns(state[:ip]) do\\r\\n :ok -> %State{state | dns_ip: state[:ip]}\\r\\n :error -> state\\r\\n end\\r\\n end\\r\\n\\r\\n @spec ensure_dns_updated(State) :: State\\r\\n def ensure_dns_updated(state) do\\r\\n Logger.info(\\\"same ip\\\")\\r\\n state\\r\\n end\\r\\n\\r\\n @impl GenServer\\r\\n def handle_info(:run, state) do\\r\\n new_state = get_ip(state)\\r\\n |> ensure_dns_updated()\\r\\n {:noreply, new_state}\\r\\n end\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" A couple improvements I have considered are adding an uninterruptible power supply and a more robust alerts system. The closet laptop server has a bad battery and the Raspberry Pi cannot collect data without electricity. While the Raspberry Pi restarts fine when electricity returns, including all services discussed in this post, the laptop requires manual intervention to bring it back online. At the time this project was implemented, Grafana\\u2019s alerts feature was not the most intuitive for alerts like what this project needs. Additionally, there\\u2019s no clear way to show the value of a variable in an alert message. Grafana has made significant updates to its alerts system since this project was implemented, but a quick exploration did not find a way to show variable values in alert messages.<\\/p>\\n\"},{\"acf_fc_layout\":\"call_to_action\",\"theme\":\"dark\",\"text\":\"Do you need a focused, dependable team to bring your environmental sensors project to the world?\",\"has_button\":true,\"button\":{\"text\":\"Get In Touch\",\"link\":\"https:\\/\\/fullsteamlabs.com\\/contact\",\"position\":\"below\"}},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" The concepts of this setup could be applied to commercial, research, and teaching applications. Some changes and improvements for commercial and research applications may include:<\\/p>\\n Some other situations this could be applied to might include:<\\/p>\\n The time put into this system has really paid off by saving countless baby plants, preventing replacement of a refrigerator that was acting up, and preventing food spoilage while said fridge was acting up.<\\/p>\\n We would love to hear how you might be using environmental sensors, or your ideas on what else we can do with these.<\\/p>\\n\"},{\"acf_fc_layout\":\"author\",\"author_type\":\"wp_user\",\"user\":{\"ID\":2,\"user_firstname\":\"Charles\",\"user_lastname\":\"Suggs\",\"nickname\":\"charles\",\"user_nicename\":\"charles\",\"display_name\":\"Charles Suggs\",\"user_email\":\"charles@fullsteamlabs.com\",\"user_url\":\"\",\"user_registered\":\"2019-05-20 17:41:59\",\"user_description\":\"is a co-founder and full stack developer at FullSteam Labs. He enjoys building useful tools for environmental sustainability, from firmware and servers through to the user interface.\",\"user_avatar\":\" FullSteam Labs is a small digital development team\\u2014we are two developers, one designer (me), and one office assistant. If you work in any kind of professional small team you likely have a need for each member of the team to wear many hats. Who wears which combination of hats is a significant aspect of effective collaboration.<\\/p>\\n We\\u2019ve found that in our agile-esque environment, a combination of UX (User Experience) design and project management throughout an app or web development project is essential for a smooth launch and happy stakeholders. Our empowering, non-hierarchical business structure reinforces these efforts.<\\/p>\\n Read more about our Ethos \\u2192<\\/a><\\/p>\\n I was a solo web and UX designer for many years, so when I joined the FullSteam Labs crew I had to figure out how to adjust to teamwork. The level of complexity of our projects grew, and we continued to craft and refine our processes of working together. At one point I was working on a website redesign, speaking to the stakeholder about end-to-end design and development. I explained that, after mockups, I\\u2019d be testing the development and sending her deliverables along the way, making sure the project stayed on track. She piped up and said, \\u201cOh, so you\\u2019re not just a designer, you’re a project manager too!\\u201d I realized she was right\\u2014I\\u2019d been intuitively taking on the responsibilities of that role without being fully aware of it, because I was accustomed to just making sure everything got done. I realized that this was critical and valuable work, and that our team needed to approach the role more intentionally.<\\/p>\\n\"},{\"acf_fc_layout\":\"signup\",\"heading_text\":\"Sign up here to get tools, tips, and insights delivered to your inbox.\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" One of the most important responsibilities for a UX Designer is to bridge empathy gaps between stakeholders, developers, and end users. We do that job through:<\\/p>\\n A Project Manager’s responsibilities typically include:<\\/p>\\n As we got serious about project management, we adopted project management software to create and track all the steps of the project. Having worked deep in the details of user workflows and interactions during the design process, I could help create detailed tasks for the developers with relevant notes from their various sources, as well as answer the developers’ questions as they got into the work of building out features. I could follow discussions to help ensure that development decisions made along the way aligned with our bigger picture goals and didn’t conflict with other aspects of the project. I could streamline testing of app features through clear processes with client and testers, filtering through helpful and not helpful feedback from the testers, delivering the relevant test results in an efficient way for the developers, and following the feature updates to see that see that they were finished as expected.<\\/p>\\n\"},{\"acf_fc_layout\":\"pull_quote\",\"quote\":\"Ultimately, our team was much better able to prevent incorrect assumptions, keep small but significant details from getting lost in the shuffle, and give the developers more time to focus on development.\",\"attribution\":\"\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" While I am the designated Project Manager for most of FullSteam Labs’ app development projects, the developers carry the load for details that are outside my wheelhouse. For example, Jason and Charles will scope the technical deliverables and their complexity, and build out the detailed issues in the project management board that are tech-specific.<\\/p>\\n We find that shorter, focused, frequent meetings\\u2014sprint planning, feature planning, retrospectives, and other communications\\u2014keep us all on the same page and prevent knowledge silos.<\\/p>\\n\"},{\"acf_fc_layout\":\"call_to_action\",\"theme\":\"dark\",\"text\":\"Do you need a focused, dependable team to bring your mobile app or web development project to the world?\",\"has_button\":true,\"button\":{\"text\":\"Get in Touch\",\"link\":\"https:\\/\\/fullsteamlabs.com\\/contact\",\"position\":\"below\"}},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" This close working relationship with developers means I can be certain that what they are building is what the client expects and that, even if we end up changing course on a particular feature, we are still producing an end product that achieves our goals. This relationship is also extremely helpful in my client-facing communications\\u2014I work with the developers to translate tech-heavy concepts in a way that is relevant and meaningful to the client, keeping the conversation focused on the business goals and user experiences we planned in the beginning of the project, thereby avoiding scope creep and mismatched expectations.<\\/p>\\n I\\u2019m working on a post about a recent Technical Design Workshop we held, so if you\\u2019re interested in a more in-depth look at how UX Design and Project Management come together in the early stage of a mobile app development project, stay tuned! <\\/i><\\/p>\\n\"},{\"acf_fc_layout\":\"author\",\"author_type\":\"wp_user\",\"user\":{\"ID\":3,\"user_firstname\":\"Courtney\",\"user_lastname\":\"Tiberio\",\"nickname\":\"courtney\",\"user_nicename\":\"courtney\",\"display_name\":\"Courtney Tiberio\",\"user_email\":\"courtney@fullsteamlabs.com\",\"user_url\":\"\",\"user_registered\":\"2019-05-21 18:01:12\",\"user_description\":\"is a co-owner, UX Designer, and Project Manager for FullSteam Labs. She works with stakeholders and developers, from concept to final digital product, to design for impactful and intuitive user experiences.\",\"user_avatar\":\" It’s true, we’re still going through a chip shortage, but I’m telling you now<\\/i><\\/b> is the best time to get started with Nerves<\\/a>. There are more developer experience paths, better documentation, and better target device support than ever before.<\\/p>\\n At FullSteam Labs, we see tremendous potential in Elixir Nerves for creating new technologies that solve real-world environmental and sustainability issues. Nerves enables fast paced development cycles for firmware and hardware teams to develop scalable, fault tolerant, connected IoT solutions.<\\/p>\\n In this post, we will explore an array of options for working with Nerves in various contexts. For our purposes, workflows include connecting to, interfacing with, and loading firmware updates to a Nerves target device.<\\/p>\\n Here\\u2019s a brief overview of all the options available for working with Nerves:<\\/p>\\n I\\u2019ve chosen to cover only a subset of these that I find to be the most productive in this post: Serial Cable, WiFi, iEx, Hotswap, OTP, Livebook, NervesHub. Without further ado, let\\u2019s explore some of the pathways you could choose from for your own Nerves workflow.<\\/p>\\n <\\/p>\\n Serial Cable<\\/b><\\/p>\\n Working with a serial cable allows you to open a direct console connection to the Nerves target through a terminal emulator like screen<\\/a>. One of the most popular hardware options for working with Nerves is the FTDI<\\/a> cable.<\\/p>\\n This workflow is particularly useful during early development and for debugging new hardware or development boards.<\\/em><\\/p>\\n The best part about working with a serial cable is that you get to see console output immediately. As a bonus, getting up and running with a serial cable requires no networking!<\\/p>\\n However, there are some drawbacks to this approach: this approach includes wires, TX and RX connections can be easily confused, the console experience (often) lacks a scrollbar, and these cables are not always available when you need them.<\\/p>\\n <\\/p>\\n WiFi<\\/b><\\/p>\\n There are a LOT of ways your Nerves target can be configured to use WiFi. Vintage Net<\\/a> provides support for all the common and some rather uncommon WiFi configurations.<\\/p>\\n The best use cases for WiFi include hobby projects, prototyping, and commercial use (although LTE is often preferred over WiFi).<\\/p>\\n The benefits of using WiFi include no wires (obviously, but hooray!), it\\u2019s commonly available, the Vintage Net package makes configuration a breeze, and Vintage Net Wizard<\\/a> provides a tool to help you get up and running with zero firmware configuration (in case that\\u2019s what you\\u2019re looking for).<\\/p>\\n However, even with Vintage Net’s help, wireless networking may present confusing situations and depending on your network connection, firmware size, and the current resource load on the target device over the air (OTA) updates may take longer than simply burning and swapping the SD card.<\\/p>\\n <\\/p>\\n iEx<\\/b><\\/p>\\n Nerves makes it easy to establish an iEx<\\/a> session with the target over ssh, which opens up the world of REPL-driven development. It\\u2019s a fantastic way to debug, inspect, and run code from the runtime environment.<\\/p>\\n The best time to use iEx as part of your Nerves workflow is always<\\/i>. It\\u2019s an indispensable tool that most of the other development workflows are steeped in.<\\/p>\\n The Nerves team has pared down the base Linux system significantly, but they\\u2019ve provided the essentials for working at the command line (like cd, dmesg, grep, ls, ping, etc) in a package called Toolshed<\\/a> that\\u2019s included by default in remote iEx sessions. In order to update code you\\u2019re working on, you can copy and paste modules and functions directly into iEx to redefine that code in memory.<\\/p>\\n While it\\u2019s great to be able to redefine code in memory with iEx copy pasta, be warned that you could code yourself into a corner so to speak.<\\/p>\\n\"},{\"acf_fc_layout\":\"signup\",\"heading_text\":\"Sign up here to get tools, tips, and insights delivered to your inbox.\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\" Hotswap<\\/b><\\/p>\\n This magical package provides OTA module hotswapping to update application code in memory. It’s not well known, but it’s my personal favorite for developing Nerves projects: kentaro\\/mix_tasks_upload_hotswap<\\/a>.<\\/p>\\n The most appropriate use cases for this workflow include project development, library development, and hardware hacking.<\\/p>\\n The benefits include, again, no wires, a very tight feedback loop (with compile-time checks), relatively easy setup, and the potential to combine with OTP for quick iteration on supervision trees and other hard to test app functions (read more about this below).<\\/p>\\n There are however, a few important drawbacks to be aware of:<\\/p>\\n <\\/p>\\n OTP<\\/b><\\/p>\\n Ah yes, OTP<\\/a>, that special blend of powerful abstractions rolled into a small set of battle-tested Erlang libraries. Although OTP isn\\u2019t specifically a \\u201cworkflow\\u201d for Nerves, it can be used in combination with module hot swapping or iEx to achieve an incredible developer experience.<\\/p>\\n With supervision trees and GenServers, it\\u2019s commonly necessary to debug issues with a fresh startup after making changes. When using mix_tasks_upload_hotswap<\\/a> with OTP primitives, you can update your project code without restarting the target device and instantly load it by restarting GenServers and\\/or Supervisors in place. This is especially useful in a scenario where you\\u2019re trying to tweak the startup sequence on a Nerves target that requires complex initialization, registration, or provisioning tasks.<\\/p>\\n Oh, did you expect to read about drawbacks to OTP? Better luck next time\\u2026<\\/p>\\n <\\/p>\\n Livebook<\\/b><\\/p>\\n Elixir Livebook<\\/a> is a library for managing live, interactive, and collaborative code notebooks. Nerves distributes pre-built firmware releases<\\/a> that run Livebook along with some demo notebooks on any supported Nerves target.<\\/p>\\n Common use cases for this workflow include experimenting with new hardware, creating documentation and guides, onboarding developers, and demos!<\\/p>\\n There are many benefits to this workflow, but first and foremost, it’s a live journal that runs on Nerves! It provides a tight feedback loop for working with reproducible, collaborative code notebooks. Livebook also opens up new possibilities for displaying data through libraries like Kino<\\/a>, automated workflows for database queries, and machine learning.<\\/p>\\n Since Livebook is designed for code editing and execution, there are some important security considerations, most notably that notebooks are not sandboxed. In production mode, a security token is required to access Livebook, but that doesn\\u2019t mean you can\\u2019t make a mess. However, if you’re careful about how you use and deploy Livebooks, you should be fine. One last thing to be aware of is that you may end up overwriting your notebooks by burning new firmware to the SD card.<\\/p>\\n <\\/p>\\n NervesHub<\\/b><\\/p>\\n The official NervesHub documentation<\\/a> describes the project as “An open-source firmware updates server”. However, there\\u2019s SO much more to it, including a CLI tool, a certificate authority server, a device updates server, an API server, and a web front end.<\\/p>\\n\"},\"name\":\"\",\"image\":false,\"bio\":\"\"},{\"acf_fc_layout\":\"author\",\"author_type\":\"wp_user\",\"user\":{\"ID\":4,\"user_firstname\":\"Jason\",\"user_lastname\":\"Johnson\",\"nickname\":\"jason\",\"user_nicename\":\"jason\",\"display_name\":\"Jason Johnson\",\"user_email\":\"jason@fullsteamlabs.com\",\"user_url\":\"\",\"user_registered\":\"2019-05-21 18:01:44\",\"user_description\":\"is a co-founder of FullSteam Labs working as a software developer. He enjoys problem solving at all levels of the tech stack, from the front-end down to embedded firmware development.\",\"user_avatar\":\"
\"},\"name\":\"\",\"image\":false,\"bio\":\"\"}],\"wp_constructor\":false}"},{"postId":785,"slug":"environmental-sensors-early-warning-system-at-home","title":"Environmental Sensors Early Warning System at Home","content":"","featuredImage":{"sourceUrl":"https://rocket.fullsteamlabs.com/wp-content/uploads/2022/07/historical-data.png","mediaDetails":{"sizes":[{"name":"medium","file":"historical-data-575x233.png"},{"name":"thumbnail","file":"historical-data-150x150.png"}]}},"termNames":["Uncategorized"],"seo":"{\"title\":\"Environmental Sensors Early Warning System at Home\",\"tags\":{\"http-equiv\":{\"x-ua-compatible\":\"ie=edge\"},\"itemprop\":{\"name\":\"Environmental Sensors Early Warning System at Home\",\"description\":\"An environmental sensor network and early warning system with data visualization, and dynamic DNS built with open source tools.\",\"image\":\"\"},\"name\":{\"description\":\"An environmental sensor network and early warning system with data visualization, and dynamic DNS built with open source tools.\",\"robots\":\"index,follow\",\"twitter:card\":\"summary\",\"twitter:title\":\"\",\"twitter:description\":\"An environmental sensor network and early warning system with data visualization, and dynamic DNS built with open source tools.\",\"twitter:image\":\"\",\"viewport\":\"width=device-width, initial-scale=1, shrink-to-fit=no\"},\"property\":{\"og:title\":\"\",\"og:description\":\"An environmental sensor network and early warning system with data visualization, and dynamic DNS built with open source tools.\",\"og:image\":\"\",\"og:locale\":\"en_US\",\"og:site_name\":\"FullSteam Labs\",\"og:type\":\"website\"}}}","status":"publish","acf":"{\"archive_subtitle\":\"Safeguard plant starts and refrigerated food\",\"header\":{\"main_image\":{\"ID\":786,\"id\":786,\"title\":\"Low Tunnel\",\"filename\":\"PXL_20210413_131559814-scaled-e1658780348242.jpg\",\"filesize\":947921,\"url\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/PXL_20210413_131559814-scaled-e1658780348242.jpg\",\"link\":\"https:\\/\\/rocket.fullsteamlabs.com\\/blog\\/environmental-sensors-early-warning-system-at-home\\/pxl_20210413_131559814\\/\",\"alt\":\"Baby plants under a low tunnel with a hose in the foreground.\",\"author\":\"1\",\"description\":\"\",\"caption\":\"Low tunnel with a small white sensor in the middle.\",\"name\":\"pxl_20210413_131559814\",\"status\":\"inherit\",\"uploaded_to\":785,\"date\":\"2022-07-22 16:06:19\",\"modified\":\"2022-07-22 16:07:17\",\"menu_order\":0,\"mime_type\":\"image\\/jpeg\",\"type\":\"image\",\"subtype\":\"jpeg\",\"icon\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-includes\\/images\\/media\\/default.png\",\"width\":2560,\"height\":1657,\"sizes\":{\"thumbnail\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/PXL_20210413_131559814-scaled-e1658780348242-150x150.jpg\",\"thumbnail-width\":150,\"thumbnail-height\":150,\"medium\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/PXL_20210413_131559814-scaled-e1658780348242-575x372.jpg\",\"medium-width\":575,\"medium-height\":372,\"medium_large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/PXL_20210413_131559814-scaled-e1658780348242-768x497.jpg\",\"medium_large-width\":768,\"medium_large-height\":497,\"large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/PXL_20210413_131559814-scaled-e1658780348242-1024x663.jpg\",\"large-width\":1024,\"large-height\":663,\"1536x1536\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/PXL_20210413_131559814-scaled-e1658780348242-1536x994.jpg\",\"1536x1536-width\":1536,\"1536x1536-height\":994,\"2048x2048\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/07\\/PXL_20210413_131559814-scaled-e1658780348242-2048x1326.jpg\",\"2048x2048-width\":2048,\"2048x2048-height\":1326}},\"subtitle\":\"\"},\"sections\":[{\"acf_fc_layout\":\"paragraph_text\",\"text\":\"
Collecting Environmental Data over Bluetooth Low Energy (BLE)<\\/h2>\\n\"},{\"acf_fc_layout\":\"paragraph_text\",\"text\":\"
Not wanting to spend money on cheaply made proprietary tech at the garden store, I looked to open source. Ruuvi tags<\\/a> are an excellent and affordable consumer grade option for Bluetooth LE connected environmental sensors inside a mostly water resistant case. Leveraging the RuuviCollector<\\/a> utility hosted on a Raspberry Pi, data collection is easy.<\\/p>\\n
Storing and Visualizing Environmental Data<\\/h2>\\n
Custom Alerting based on Data<\\/h2>\\n
Checking Data from Outside the Network<\\/h2>\\n
Opportunities for Improvement<\\/h2>\\n
Commercialization and Larger, More Resilient Implementations<\\/h2>\\n
\\n
\\n
\"},\"name\":\"\",\"image\":false,\"bio\":\"\"}],\"wp_constructor\":false}"},{"postId":717,"slug":"ux-design-project-management-for-success","title":"UX Design + Project Management for Success","content":"","featuredImage":{"sourceUrl":"https://rocket.fullsteamlabs.com/wp-content/uploads/2022/06/Screen-Shot-2022-06-14-at-4.45.15-PM.png","mediaDetails":{"sizes":[{"name":"medium","file":"Screen-Shot-2022-06-14-at-4.45.15-PM-575x254.png"},{"name":"large","file":"Screen-Shot-2022-06-14-at-4.45.15-PM-1024x452.png"},{"name":"thumbnail","file":"Screen-Shot-2022-06-14-at-4.45.15-PM-150x150.png"},{"name":"medium_large","file":"Screen-Shot-2022-06-14-at-4.45.15-PM-768x339.png"},{"name":"1536x1536","file":"Screen-Shot-2022-06-14-at-4.45.15-PM-1536x677.png"}]}},"termNames":["Uncategorized"],"seo":"{\"title\":\"UX Design + Project Management for Success\",\"tags\":{\"http-equiv\":{\"x-ua-compatible\":\"ie=edge\"},\"itemprop\":{\"name\":\"UX Design + Project Management for Success\",\"description\":\"How our design and development teams work together to keep mobile app and web development projects on track and ensure success for our clients.\",\"image\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM.png\"},\"name\":{\"description\":\"How our design and development teams work together to keep mobile app and web development projects on track and ensure success for our clients.\",\"robots\":\"index,follow\",\"twitter:card\":\"summary\",\"twitter:title\":\"\",\"twitter:description\":\"How our design and development teams work together to keep mobile app and web development projects on track and ensure success for our clients.\",\"twitter:image\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM.png\",\"viewport\":\"width=device-width, initial-scale=1, shrink-to-fit=no\"},\"property\":{\"og:title\":\"\",\"og:description\":\"How our design and development teams work together to keep mobile app and web development projects on track and ensure success for our clients.\",\"og:image\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM.png\",\"og:locale\":\"en_US\",\"og:site_name\":\"FullSteam Labs\",\"og:type\":\"website\"}}}","status":"publish","acf":"{\"archive_subtitle\":\"Small Digital Development Team Roles\",\"header\":{\"main_image\":{\"ID\":727,\"id\":727,\"title\":\"Screen Shot 2022-06-14 at 4.45.15 PM\",\"filename\":\"Screen-Shot-2022-06-14-at-4.45.15-PM.png\",\"filesize\":1227686,\"url\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM.png\",\"link\":\"https:\\/\\/rocket.fullsteamlabs.com\\/blog\\/ux-design-project-management-for-success\\/screen-shot-2022-06-14-at-4-45-15-pm\\/\",\"alt\":\"Many hats: UX design and project management in an environmental sustainability tech team\",\"author\":\"1\",\"description\":\"\",\"caption\":\"\",\"name\":\"screen-shot-2022-06-14-at-4-45-15-pm\",\"status\":\"inherit\",\"uploaded_to\":717,\"date\":\"2022-06-21 20:35:54\",\"modified\":\"2022-06-21 20:37:01\",\"menu_order\":0,\"mime_type\":\"image\\/png\",\"type\":\"image\",\"subtype\":\"png\",\"icon\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-includes\\/images\\/media\\/default.png\",\"width\":2032,\"height\":896,\"sizes\":{\"thumbnail\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM-150x150.png\",\"thumbnail-width\":150,\"thumbnail-height\":150,\"medium\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM-575x254.png\",\"medium-width\":575,\"medium-height\":254,\"medium_large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM-768x339.png\",\"medium_large-width\":768,\"medium_large-height\":339,\"large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM-1024x452.png\",\"large-width\":1024,\"large-height\":452,\"1536x1536\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM-1536x677.png\",\"1536x1536-width\":1536,\"1536x1536-height\":677,\"2048x2048\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/Screen-Shot-2022-06-14-at-4.45.15-PM.png\",\"2048x2048-width\":2032,\"2048x2048-height\":896}},\"subtitle\":\"\"},\"sections\":[{\"acf_fc_layout\":\"paragraph_text\",\"text\":\"
From Intuitive to Intentional Project Management<\\/h2>\\n
The Intersection of UX Design and Project Management<\\/h2>\\n
\\n
\\n
Sharing the Project Management Load with Software Developers<\\/h2>\\n
\"},\"name\":\"\",\"image\":false,\"bio\":\"\"}],\"wp_constructor\":false}"},{"postId":712,"slug":"workflows-for-elixir-nerves","title":"Workflows for Elixir Nerves","content":"","featuredImage":{"sourceUrl":"https://rocket.fullsteamlabs.com/wp-content/uploads/2022/06/harrison-broadbent-c3YpscwJb04-unsplash-scaled.jpg","mediaDetails":{"sizes":[{"name":"medium","file":"harrison-broadbent-c3YpscwJb04-unsplash-575x324.jpg"},{"name":"large","file":"harrison-broadbent-c3YpscwJb04-unsplash-1024x576.jpg"},{"name":"thumbnail","file":"harrison-broadbent-c3YpscwJb04-unsplash-150x150.jpg"},{"name":"medium_large","file":"harrison-broadbent-c3YpscwJb04-unsplash-768x432.jpg"},{"name":"1536x1536","file":"harrison-broadbent-c3YpscwJb04-unsplash-1536x864.jpg"},{"name":"2048x2048","file":"harrison-broadbent-c3YpscwJb04-unsplash-2048x1152.jpg"}]}},"termNames":["Uncategorized"],"seo":"{\"title\":\"Workflows for Elixir Nerves\",\"tags\":{\"http-equiv\":{\"x-ua-compatible\":\"ie=edge\"},\"itemprop\":{\"name\":\"Workflows for Elixir Nerves\",\"description\":\"Find your most productive workflow when working with Nerves in this overview of the landscape.\",\"image\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-scaled.jpg\"},\"name\":{\"description\":\"Find your most productive workflow when working with Nerves in this overview of the landscape.\",\"robots\":\"index,follow\",\"twitter:card\":\"summary\",\"twitter:title\":\"Workflows for Elixir Nerves\",\"twitter:description\":\"Find your most productive workflow when working with Nerves in this overview of the landscape.\",\"twitter:image\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-scaled.jpg\",\"viewport\":\"width=device-width, initial-scale=1, shrink-to-fit=no\"},\"property\":{\"og:title\":\"Workflows for Elixir Nerves\",\"og:description\":\"Find your most productive workflow when working with Nerves in this overview of the landscape.\",\"og:image\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-scaled.jpg\",\"og:locale\":\"en_US\",\"og:site_name\":\"FullSteam Labs\",\"og:type\":\"website\"}}}","status":"publish","acf":"{\"archive_subtitle\":\"Find your most productive workflow!\",\"header\":{\"main_image\":{\"ID\":759,\"id\":759,\"title\":\"harrison-broadbent-c3YpscwJb04-unsplash\",\"filename\":\"harrison-broadbent-c3YpscwJb04-unsplash-scaled.jpg\",\"filesize\":593045,\"url\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-scaled.jpg\",\"link\":\"https:\\/\\/rocket.fullsteamlabs.com\\/blog\\/workflows-for-elixir-nerves\\/harrison-broadbent-c3ypscwjb04-unsplash\\/\",\"alt\":\"\",\"author\":\"1\",\"description\":\"\",\"caption\":\"\",\"name\":\"harrison-broadbent-c3ypscwjb04-unsplash\",\"status\":\"inherit\",\"uploaded_to\":712,\"date\":\"2022-06-22 20:32:12\",\"modified\":\"2022-06-22 20:32:12\",\"menu_order\":0,\"mime_type\":\"image\\/jpeg\",\"type\":\"image\",\"subtype\":\"jpeg\",\"icon\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-includes\\/images\\/media\\/default.png\",\"width\":2560,\"height\":1441,\"sizes\":{\"thumbnail\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-150x150.jpg\",\"thumbnail-width\":150,\"thumbnail-height\":150,\"medium\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-575x324.jpg\",\"medium-width\":575,\"medium-height\":324,\"medium_large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-768x432.jpg\",\"medium_large-width\":768,\"medium_large-height\":432,\"large\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-1024x576.jpg\",\"large-width\":1024,\"large-height\":576,\"1536x1536\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-1536x864.jpg\",\"1536x1536-width\":1536,\"1536x1536-height\":864,\"2048x2048\":\"https:\\/\\/rocket.fullsteamlabs.com\\/wp-content\\/uploads\\/2022\\/06\\/harrison-broadbent-c3YpscwJb04-unsplash-2048x1152.jpg\",\"2048x2048-width\":2048,\"2048x2048-height\":1152}},\"subtitle\":\"\"},\"sections\":[{\"acf_fc_layout\":\"paragraph_text\",\"text\":\"
\\n
\\n