From 22b54c800bad771d3d1d2d52c71592c5339b103d Mon Sep 17 00:00:00 2001 From: connor rigby Date: Sun, 26 Feb 2017 23:33:59 -0800 Subject: [PATCH] update docs --- CONTRIBUTING.md => .github/CONTRIBUTING.md | 0 .../ISSUE_TEMPLATE.md | 0 .../PULL_REQUEST_TEMPLATE.md | 0 BUILDING.md | 157 ------------------ CHANGELOG.md | 24 +++ README.md | 2 +- apps/farmbot/config/config.exs | 13 +- apps/farmbot/config/hardware/rpi/hardware.exs | 4 + .../farmbot/config/hardware/rpi2/hardware.exs | 4 + .../farmbot/config/hardware/rpi3/hardware.exs | 4 + apps/farmbot/lib/bot_state/supervisor.ex | 4 +- apps/farmbot/lib/database/database.ex | 6 +- apps/farmbot/lib/database/syncable.ex | 11 +- apps/farmbot/lib/farmbot.ex | 3 +- .../lib/mix/tasks/new_celery_script.ex | 1 + apps/farmbot/lib/mix/tasks/upload.ex | 1 + apps/farmbot/lib/mix/tasks/warning.ex | 1 + .../lib/system/farmbot_system_supervisor.ex | 15 +- apps/farmbot/lib/system/redis/redis_server.ex | 4 +- apps/farmbot/mix.exs | 17 +- docs/BUILDING.md | 39 +++++ docs/ENVIRONMENT.md | 35 ++++ FAQ.md => docs/FAQ.md | 19 ++- docs/farmbot_logo.png | Bin 0 -> 13312 bytes 24 files changed, 169 insertions(+), 195 deletions(-) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (100%) rename ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md (100%) rename PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md (100%) delete mode 100644 BUILDING.md create mode 100644 docs/BUILDING.md create mode 100644 docs/ENVIRONMENT.md rename FAQ.md => docs/FAQ.md (79%) create mode 100644 docs/farmbot_logo.png diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md similarity index 100% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/BUILDING.md b/BUILDING.md deleted file mode 100644 index d5c1b407..00000000 --- a/BUILDING.md +++ /dev/null @@ -1,157 +0,0 @@ -# Advanced Things that you shouldn't need to worry about. - -## Building an image -Building a `*.img` file is rather simple, its one step farther than the regular build. - -```bash -cd ~/farmbot/os/apps/farmbot -export MIX_ENV=prod -export NERVES_TARGET= -# THIS COULD TAKE A WHILE DEPENDING ON YOUR MACHINE -mix firmware -# this is the actual .img generation, it should go pretty quick. -fwup -a -d _images/farmbot//farmbot.img -i \ - _images/farmbot//farmbot.fw -t complete -``` - -## Documentation - -Documentation isn't stored in source control, because it is large and changes frequently. [Raise an issue](https://github.com/FarmBot/farmbot_os/issues/new) if you would like them published on HEX. - -To view documentation: - -```bash -cd ~/farmbot/os -MIX_ENV=dev mix deps.get -MIX_ENV=dev mix docs -``` - -## Tests - -Tests coverage is a work in progress and all help is appreciated! - -```bash -export MIX_ENV=test -mix deps.get -mix test -``` - -## Code Styling and Consistency - -Credo doesn't work very well on umbrella projects, so I only enforce credo on -the main application. - -Note: this will show all #TODOS in the application. - -```bash -cd ~/farmbot/os/apps/farmbot -export MIX_ENV=dev -mix deps.get -mix credo --strict -``` - - - -## Building the Linux RootFS - -```bash -cd ~/farmbot/os -# this should set up the environment needed to build the system -export NERVES_TARGET= -make create-build-${NERVES_TARGET} - -# Now that the environment is set up you should change into the new dir it told you too. -cd apps/NERVES_SYSTEM_${NERVES_TARGET} -# from this dir we have a few different things we can configure in buildroot. - -# To add/remove linux packages. -make menuconfig - -# To add/remove busybox packages. -make busybox-menuconfig - -# To add/enable/disable features in the kernel. -make linux-menuconfig - -# Save those configs - -# For buildroot packages -make savedefconfig - -# For busybox config -cp build/busybox*/.config ../nerves_system_*/busybox_defconfig - -# for linux config -cp build/linux*/.config ../nerves_system_*/linux-*.defconfig - -# To make your configuration -make - -# that will take a while depending on your machine. You will see the build output in your terminal. -# on a core i7, 32 gigs or ram, nvme ssd machine, it takes about 15-20 minutes depending on if ccache is enabled. - -# when the rootfs build finishes to use said rootfs in building firmware: -cd ../farmbot - -mix firmware -# to burn to an sdcard. (this requires sudo) -mix firmware.burn - -``` - -## Porting to a New System - -There are several options for porting FarmBot to non-Raspberry Pi systems. One option is to use the RPI3 system as a template: - -```bash -cd ~/os/farmbot/os/apps -mix new nerves_system_newboard -``` - -At bare minimum you will need the following files files, using the RPI3 system as a template: - -``` -nerves_system_newboard -├── rootfs-additions -├── busybox_defconfig -├── fwup.conf -├── linux-VERSION.defconfig -├── mix.exs -├── nerves.exs -└── nerves_defconfig -``` - -Lets go thru those files and folders and explain what each one is. - -* `rootfs-additions` - thechnically this is optional but that will never happen. there is usually some prop files needed like wireless firmware, erlinit config etc. -* `busybox_defconfig` - This file has extra configs for Farmbot System. We require a few extra packages in busybox to be enabled. - * mkfs.ext -* `fwup.conf` - [FwUp](https://github.com/fhunleth/fwup) is how farmbot updates and builds its firmware. This can be configured how ever your platfrom requires, but we require a few basic things - * one squashfs system partition which will ALWAYS read only. - * one data partition. This can be any filesystem as long as it supports symbolic links. It will be mounted read only MOST of the time -* `linux.defconfig` - The configuration for the Linux Kernel on this board. -* `mix.exs` - a description of this project. requires a bit of configuration. see rpi3 system for example. -* `nerves.exs` - configures a nerves target. See RPI3 System for examples. -* `nerves_defconfig` - The buildroot configuration. Needs a bit of specifics. -``` -buildroot -├── packages -| ├── networking -| | ├── hostapd -| | ├── dnsmasq -| | ├── dropbear -| | └── iw -| | -| └── hardware handling -| └── avrdude -| -└── filesystem - └── squashfs -``` diff --git a/CHANGELOG.md b/CHANGELOG.md index 93fe7412..a9a814f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,3 +9,27 @@ # 2.1.10 * a few minor bug fixes to the previous release. + +# 3.0.0 +* Makefile + +# 3.0.1 +* implement bot state migrations +* logger fixes. + +# 3.0.2 +* Farmware initial concepts. + +# 3.0.3 +* Farmware fixes + +# 3.0.4 +* Logger bug fixes + +# 3.0.5 +* Configurator got a facelift + a few extra features. + +# 3.0.6 +* Syncing is now a multiple request action and is now much faster and safer. +* change folder structure +* begin adding redis support. diff --git a/README.md b/README.md index 9b8bf67a..df5fd680 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ To update the firmware on the Raspberry Pi (Only version 3 for now) and the Ardu # Problems? -See the [FAQ](FAQ.md) +See the [FAQ](faq.html) If your problem isn't solved there please file an issue on [Github](https://github.com/FarmBot/farmbot_os/issues/new) # Want to Help? diff --git a/apps/farmbot/config/config.exs b/apps/farmbot/config/config.exs index 21b8c7eb..faf22071 100644 --- a/apps/farmbot/config/config.exs +++ b/apps/farmbot/config/config.exs @@ -3,6 +3,7 @@ use Mix.Config target = Mix.Project.config[:target] env = Mix.env() mqtt_transport = Farmbot.Transport.GenMqtt +redis_transport = Farmbot.Transport.Redis config :logger, utc_log: true @@ -16,12 +17,12 @@ config :iex, :colors, enabled: true config :farmbot, auth_callbacks: [mqtt_transport] # frontend <-> bot transports. -config :farmbot, transports: [mqtt_transport] +config :farmbot, transports: [mqtt_transport, redis_transport] -config :farmbot, redis_port: 6379 - -config :nerves, :firmware, - rootfs_additions: "config/hardware/#{target}/rootfs-additions-#{env}" +# give the ability to start a redis server instance in dev mode. +config :farmbot, :redis, + server: System.get_env("REDIS_SERVER") || false, + port: System.get_env("REDIS_SERVER_PORT") || 6379 # This is usually in the `priv` dir of :tzdata, but our fs is read only. config :tzdata, :data_dir, "/tmp" @@ -33,3 +34,5 @@ import_config "#{env}.exs" # import config specific to our nerves_target IO.puts "using #{target} - #{env} configuration." import_config "hardware/#{target}/hardware.exs" +config :nerves, :firmware, + rootfs_additions: "config/hardware/#{target}/rootfs-additions-#{env}" diff --git a/apps/farmbot/config/hardware/rpi/hardware.exs b/apps/farmbot/config/hardware/rpi/hardware.exs index 3c2de68b..78a56a6a 100644 --- a/apps/farmbot/config/hardware/rpi/hardware.exs +++ b/apps/farmbot/config/hardware/rpi/hardware.exs @@ -4,3 +4,7 @@ config :farmbot, config_file_name: "default_config_rpi.json", configurator_port: 80, streamer_port: 4040 + +config :farmbot, :redis, + server: true, + port: 6379 diff --git a/apps/farmbot/config/hardware/rpi2/hardware.exs b/apps/farmbot/config/hardware/rpi2/hardware.exs index e244f22d..b70a80ed 100644 --- a/apps/farmbot/config/hardware/rpi2/hardware.exs +++ b/apps/farmbot/config/hardware/rpi2/hardware.exs @@ -4,3 +4,7 @@ config :farmbot, config_file_name: "default_config_rpi2.json", configurator_port: 80, streamer_port: 4040 + +config :farmbot, :redis, + server: true, + port: 6379 diff --git a/apps/farmbot/config/hardware/rpi3/hardware.exs b/apps/farmbot/config/hardware/rpi3/hardware.exs index 72ac4718..e09d0985 100644 --- a/apps/farmbot/config/hardware/rpi3/hardware.exs +++ b/apps/farmbot/config/hardware/rpi3/hardware.exs @@ -4,3 +4,7 @@ config :farmbot, config_file_name: "default_config_rpi3.json", configurator_port: 80, streamer_port: 4040 + +config :farmbot, :redis, + server: true, + port: 6379 diff --git a/apps/farmbot/lib/bot_state/supervisor.ex b/apps/farmbot/lib/bot_state/supervisor.ex index 3db0ee88..355805e2 100644 --- a/apps/farmbot/lib/bot_state/supervisor.ex +++ b/apps/farmbot/lib/bot_state/supervisor.ex @@ -1,7 +1,7 @@ defmodule Farmbot.BotState.Supervisor do @moduledoc """ - Supervises the state tracker modules and an event manager that other - things can subscribe too. + Supervises the state tracker modules and an event manager that other + things can subscribe too. """ use Supervisor diff --git a/apps/farmbot/lib/database/database.ex b/apps/farmbot/lib/database/database.ex index d9de4134..215c3357 100644 --- a/apps/farmbot/lib/database/database.ex +++ b/apps/farmbot/lib/database/database.ex @@ -1,9 +1,9 @@ defmodule Farmbot.Sync do @moduledoc """ There is a quite a bit of macros going on here. - * `defdatabase` comes from `Amnesia` - * defindes a database. This should only show up once. - * syncable comes from `Syncable` and defines a database table. + * defdatabase comes from Amnesia + * defindes a database. This should only show up once. + * syncable comes from Syncable and defines a database table. """ use Amnesia diff --git a/apps/farmbot/lib/database/syncable.ex b/apps/farmbot/lib/database/syncable.ex index 857ddb61..b5eff4f0 100644 --- a/apps/farmbot/lib/database/syncable.ex +++ b/apps/farmbot/lib/database/syncable.ex @@ -1,13 +1,6 @@ defmodule Syncable do @moduledoc ~s""" - Creates a syncable object from Farmbots rest api. - Example: - iex> defmodule BubbleGum do - ...> use Syncable, name: __MODULE__, model: [:flavors, :brands] - ...> end - iex> BubbleGum.create!(%{"flavors" => ["mint", "berry"], - ..> "brands" => ["BigRed"]}) - {:ok, %BubbleGum{flavors: ["mint", "berry"], brands: ["BigRed"]}} + Defines the structs and amnesia tables for a Farmbot Syncable object """ use Amnesia @@ -149,7 +142,7 @@ defmodule Syncable do end @doc """ - Same as `enter_into_db/1` but will raise errors if + Same as enter_into_db/1 but will raise errors if problems are encountered. """ def enter_into_db!(list_or_object) diff --git a/apps/farmbot/lib/farmbot.ex b/apps/farmbot/lib/farmbot.ex index 16421c10..e2c09c95 100644 --- a/apps/farmbot/lib/farmbot.ex +++ b/apps/farmbot/lib/farmbot.ex @@ -48,9 +48,10 @@ defmodule Farmbot do end @doc """ - Starts the Farmbot Application + Entry Point to Farmbot """ @spec start(atom, [any]) :: {:ok, pid} + def start(type, args) def start(_, [args]) do Logger.info ">> init!" Amnesia.start diff --git a/apps/farmbot/lib/mix/tasks/new_celery_script.ex b/apps/farmbot/lib/mix/tasks/new_celery_script.ex index 97ff0b9a..c19666d9 100644 --- a/apps/farmbot/lib/mix/tasks/new_celery_script.ex +++ b/apps/farmbot/lib/mix/tasks/new_celery_script.ex @@ -1,4 +1,5 @@ defmodule Mix.Tasks.CS.New do + @moduledoc false use Mix.Task @shortdoc "Creates a new celery script command" def run([new_cs]) do diff --git a/apps/farmbot/lib/mix/tasks/upload.ex b/apps/farmbot/lib/mix/tasks/upload.ex index 46a29232..d79704c7 100644 --- a/apps/farmbot/lib/mix/tasks/upload.ex +++ b/apps/farmbot/lib/mix/tasks/upload.ex @@ -1,4 +1,5 @@ defmodule Mix.Tasks.Farmbot.Upload do + @moduledoc false use Mix.Task alias Mix.Tasks.Firmware.Push @shortdoc "Uploads a file to a url" diff --git a/apps/farmbot/lib/mix/tasks/warning.ex b/apps/farmbot/lib/mix/tasks/warning.ex index 8aec0577..db9288d1 100644 --- a/apps/farmbot/lib/mix/tasks/warning.ex +++ b/apps/farmbot/lib/mix/tasks/warning.ex @@ -1,4 +1,5 @@ defmodule Mix.Tasks.Farmbot.Warning do + @moduledoc false use Mix.Task def run(_), do: Mix.raise("Please export a MIX_TARGET") end diff --git a/apps/farmbot/lib/system/farmbot_system_supervisor.ex b/apps/farmbot/lib/system/farmbot_system_supervisor.ex index 6100262d..febad916 100644 --- a/apps/farmbot/lib/system/farmbot_system_supervisor.ex +++ b/apps/farmbot/lib/system/farmbot_system_supervisor.ex @@ -3,6 +3,7 @@ defmodule Farmbot.System.Supervisor do Supervises Platform specific stuff for Farmbot to operate """ use Supervisor + @redis_config Application.get_all_env(:farmbot)[:redis] def start_link(args) do Supervisor.start_link(__MODULE__, [args], name: __MODULE__) @@ -12,11 +13,19 @@ defmodule Farmbot.System.Supervisor do children = [ worker(Farmbot.System.FS, [target], restart: :permanent), worker(Farmbot.System.FS.Worker, [target], restart: :permanent), - worker(Redis.Server, [], restart: :permanent), worker(Farmbot.System.FS.ConfigStorage, [], restart: :permanent), - worker(Farmbot.System.Network, [target], restart: :permanent), - ] + worker(Farmbot.System.Network, [target], restart: :permanent) + ] ++ maybe_redis() opts = [strategy: :one_for_one] supervise(children, opts) end + + @spec maybe_redis :: [Supervisor.Spec.spec] + defp maybe_redis do + if @redis_config[:server] do + [worker(Redis.Server, [], restart: :permanent)] + else + [] + end + end end diff --git a/apps/farmbot/lib/system/redis/redis_server.ex b/apps/farmbot/lib/system/redis/redis_server.ex index 92b17354..61ae35e1 100644 --- a/apps/farmbot/lib/system/redis/redis_server.ex +++ b/apps/farmbot/lib/system/redis/redis_server.ex @@ -2,7 +2,7 @@ defmodule Redis.Server do @moduledoc """ Port for a redis server """ - @port Application.get_env(:farmbot, :redis_port) + @config Application.get_all_env(:farmbot)[:redis] if Mix.env() == :dev do def should_bind, do: "bind 0.0.0.0" @@ -13,7 +13,7 @@ defmodule Redis.Server do def config(path: dir), do: ~s""" #{should_bind()} protected-mode yes - port #{@port} + port #{@config[:port]} tcp-backlog 511 unixsocket /tmp/redis.sock unixsocketperm 700 diff --git a/apps/farmbot/mix.exs b/apps/farmbot/mix.exs index b0a8ca74..3e16bd18 100644 --- a/apps/farmbot/mix.exs +++ b/apps/farmbot/mix.exs @@ -14,6 +14,8 @@ defmodule Farmbot.Mixfile do def project do [app: :farmbot, + description: "The Brains of the Farmbot Project", + package: package(), test_coverage: [tool: ExCoveralls], version: @version, target: @target, @@ -34,11 +36,20 @@ defmodule Farmbot.Mixfile do source_url: "https://github.com/Farmbot/farmbot_os", homepage_url: "http://farmbot.io", docs: [main: "Farmbot", - logo: "priv/static/farmbot_logo.png", - extras: ["../../README.md", "../../BUILDING.md"]] + logo: "../../docs/farmbot_logo.png", + extras: ["../../docs/BUILDING.md", + "../../docs/FAQ.md", + "../../docs/ENVIRONMENT.md", + "../../README.md"]] ] end + def package do + [name: "Farmbot OS", + maintainers: "Farmbot.io", + licenses: "MIT"] + end + def application do [mod: {Farmbot, @@ -144,7 +155,7 @@ defmodule Farmbot.Mixfile do # this is for cross compilation to work # New version of nerves might not need this? defp aliases("host"), do: [ - "firmware": ["farmbot.warning"], + "firmware": ["compile"], "firmware.push": ["farmbot.warning"], "credo": ["credo list --only readability,warning,todo,inspect,refactor --ignore-checks todo,spec"], "test": ["test", "credo"]] diff --git a/docs/BUILDING.md b/docs/BUILDING.md new file mode 100644 index 00000000..3ece888d --- /dev/null +++ b/docs/BUILDING.md @@ -0,0 +1,39 @@ +# Building an Image from source +This project is written in the programming language Elixir and built using the +Nerves Project framework. + +## Before you begin +You will need a number of things before we start. +* A Linux machine +* A x64 bit machine +* probably about 16 gigs of ram +* about ~30 gigs of hard drive space +* a fairly recent cpu + +## Install dependencies +If you have the above set up you will need some software dependencies. +* Elixir found [here](http://elixir-lang.org/install.html) +* Nerves Bootstrapper found [here](https://hexdocs.pm/nerves/installation.html#Linux) +* NodeJS found [here](https://nodejs.org/en/download/) + +## Set up environment +We are going to set up the environment for building Farmbot OS +```bash +mkdir farmbot +cd farmbot +git clone https://github.com/FarmBot/farmbot_os.git os +cd os +npm install +``` + +## Compile the application +From here you will have to choose your own adventure. You get to choose if you +want development mode or production mode, and you get to choose the target you +want to build the executables for. see the [ENVIRONMENT](environment.html) for more details +This will take a while depending on you machine. +```bash +export MIX_ENV=prod +export MIX_TARGET=rpi3 +mix deps.get +mix firmware +``` diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md new file mode 100644 index 00000000..581e2b80 --- /dev/null +++ b/docs/ENVIRONMENT.md @@ -0,0 +1,35 @@ +# Farmbot Build Environment +There are a number of things that can (and should) be configured at compile time +via shell environment variables. + +## Mix Environment +you can set `MIX_ENV=prod` or `MIX_ENV=dev` (default) to change the environment +of the farmbot application. + +## Mix Target +When building firmware you can set the target. +`MIX_TARGET=rpi2` + +## Webpack +Farmbot's `Configurator` application uses Webpack to compile a TypeScript Project +into a static website that gets served by `Plug` + +Webpack is configured via a package called `ex_webpack`. Default behavior it to +watch the web source files for changes and recompile. This adds extra time to the +initial compile of the application and can be just generally annoying. to disable +this export `USE_WEBPACK=false` + +## Configurator +The Configurator app is started by default on port `5000` and the image streamer +is on port `5050`. these can be changed by exporting in development mode only: +```bash +CONFIGURATOR_PORT=4000 +STREAMER_PORT=4040 +``` + +## Redis Server +In development mode you can export +```bash +REDIS_SERVER=true +REDIS_SERVER_PORT=6379 +``` diff --git a/FAQ.md b/docs/FAQ.md similarity index 79% rename from FAQ.md rename to docs/FAQ.md index b9661c1b..dede2bf1 100644 --- a/FAQ.md +++ b/docs/FAQ.md @@ -1,4 +1,5 @@ -# My bot doesn't boot on a fresh SD card! +# Frequently Asked Questions +## My bot doesn't boot on a fresh SD card! This could be one of a few things. These things are in order of probability. @@ -13,32 +14,32 @@ This could be one of a few things. These things are in order of probability. * You have a bad SD Card. * You aren't using a Raspberry Pi 3 (Porting Farmbot is relatively simple). -# Can I SSH into the Farmbot? +## Can I SSH into the Farmbot? -Yes, starting with version 2.1.1. The user is root and there is no password. This may change in future versions. +Yes, starting with version `2.1.1`. The user is root and there is no password. This may change in future versions. -# Why are my SSH keys invalid? +## Why are my SSH keys invalid? Farmbot's `rootfs` file system is read only. SSH keys must be stored elsewhere. They may get lost if you pull the power to your Farmbot. Follow the directions in the shell to resolve the issue. -# Can the shell run on HDMI rather than SSH? +## Can the shell run on HDMI rather than SSH? Yes and no. HDMI will display an IEX (Elixir shell) session. You may access the shell via `ctrl+c` but this will kill Farmbot's main software. -# SSH Has No Linux Utilities. +## SSH Has No Linux Utilities. Farmbot is built using Buildroot, Which uses a very small Linux environment to minimize boot time and overhead. This gives us a Linux shell of `sh` and most utilities are provided by `busybox`. -# Why aren't [X] or [Y] packages included? +## Why aren't [X] or [Y] packages included? See the above answer. [Raise an issue](https://github.com/FarmBot/farmbot_os/issues/new) to request a package. Future versions of FarmBotOS may provide a plugin system. It is not implemented yet. -# Does Farmbot support Ethernet rather than WIFI? +## Does Farmbot support Ethernet rather than WIFI? Yes. When you log into the Farmbot Configurator wifi SSID, select `Use Ethernet`. **NOTE**: This can not be changed without a factory reset. -# How do I factory reset my bot? +## How do I factory reset my bot? This is not as trivial as you would think right now. (We are working on it.) diff --git a/docs/farmbot_logo.png b/docs/farmbot_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d0236fc22677a6d408ad37de6933e282b839ecc9 GIT binary patch literal 13312 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 literal 0 HcmV?d00001