mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-13 22:30:37 +08:00
清理文章内容
This commit is contained in:
parent
c0ca3821c8
commit
9b6dbb861a
@ -1,11 +1,11 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Astrophotography with Fedora Astronomy Lab: setting up)
|
||||
[#]: via: (https://fedoramagazine.org/astrophotography-with-fedora-astronomy-lab-setting-up/)
|
||||
[#]: author: (Geoffrey Marr https://fedoramagazine.org/author/coremodule/)
|
||||
[#]: subject: "Astrophotography with Fedora Astronomy Lab: setting up"
|
||||
[#]: via: "https://fedoramagazine.org/astrophotography-with-fedora-astronomy-lab-setting-up/"
|
||||
[#]: author: "Geoffrey Marr https://fedoramagazine.org/author/coremodule/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Astrophotography with Fedora Astronomy Lab: setting up
|
||||
======
|
||||
@ -28,21 +28,15 @@ Download Fedora Astronomy Lab from the [Fedora Labs website][4]. You will need a
|
||||
|
||||
Before you can go capturing the heavens, you need to do some minor setup in Fedora Astronomy Lab.
|
||||
|
||||
First of all, you need to add your user to the _dialout_ group so that you can access certain pieces of astronomical equipment from within the guiding software. Do that by opening the terminal (Konsole) and running this command (replacing _user_ with your username):
|
||||
First of all, you need to add your user to the *dialout* group so that you can access certain pieces of astronomical equipment from within the guiding software. Do that by opening the terminal (Konsole) and running this command (replacing *user* with your username):
|
||||
|
||||
sudo usermod -a -G dialout user
|
||||
|
||||
My personal setup includes a guide camera (QHY5 series, also known as Orion Starshoot) that does not have a driver in the mainline Fedora repositories. To enable it, ypu need to install the [qhyccd SDK][9]. (_Note that this package is not officially supported by Fedora. Use it at your own risk.)_ At the time of writing, I chose to use the latest stable release, 20.08.26. Once you download the Linux 64-bit version of the SDK, to extract it:
|
||||
```
|
||||
My personal setup includes a guide camera (QHY5 series, also known as Orion Starshoot) that does not have a driver in the mainline Fedora repositories. To enable it, ypu need to install the [qhyccd SDK][9]. (*Note that this package is not officially supported by Fedora. Use it at your own risk.)* At the time of writing, I chose to use the latest stable release, 20.08.26. Once you download the Linux 64-bit version of the SDK, to extract it:
|
||||
|
||||
```
|
||||
|
||||
tar zxvf sdk_linux64_20.08.26.tgz
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
Now change into the directory you just extracted, change the permissions of the _install.sh_ file to give you execute privileges, and run the _install.sh_:
|
||||
Now change into the directory you just extracted, change the permissions of the *install.sh* file to give you execute privileges, and run the *install.sh*:
|
||||
|
||||
```
|
||||
cd sdk_linux64_20.08.26
|
||||
@ -50,49 +44,35 @@ chmod +x install.sh
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
Now it’s time to install the qhyccd INDI driver. INDI is an open source software library used to control astronomical equipment. Unfortunately, the driver is unavailable in the mainline Fedora repositories, but it is in a Copr repository. (_Note: Copr is not officially supported by Fedora infrastructure. Use packages at your own risk.)_ If you prefer to have the newest (and perhaps unstable!) pieces of astronomy software, you can also enable the “bleeding” repositories at this time by following [this guide][10]. For this tutorial, you are only going to enable one repo:
|
||||
```
|
||||
Now it’s time to install the qhyccd INDI driver. INDI is an open source software library used to control astronomical equipment. Unfortunately, the driver is unavailable in the mainline Fedora repositories, but it is in a Copr repository. (*Note: Copr is not officially supported by Fedora infrastructure. Use packages at your own risk.)* If you prefer to have the newest (and perhaps unstable!) pieces of astronomy software, you can also enable the “bleeding” repositories at this time by following [this guide][10]. For this tutorial, you are only going to enable one repo:
|
||||
|
||||
```
|
||||
|
||||
sudo dnf copr enable xsnrg/indi-3rdparty-bleeding
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
Install the driver by running the following command:
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
sudo dnf install indi-qhy
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
Finally, update all of your system packages:
|
||||
|
||||
sudo dnf update -y
|
||||
|
||||
To recap what you accomplished in this sectio: you added your user to the _dialout_ group, downloaded and installed the qhyccd driver, enabled the _indi-3rdparty-bleeding_ copr, installed the qhyccd-INDI driver with dnf, and updated your system.
|
||||
To recap what you accomplished in this sectio: you added your user to the *dialout* group, downloaded and installed the qhyccd driver, enabled the *indi-3rdparty-bleeding* copr, installed the qhyccd-INDI driver with dnf, and updated your system.
|
||||
|
||||
### Connecting your equipment
|
||||
|
||||
This is the time to connect all your equipment to your computer. Most astronomical equipment will connect via USB, and it’s really as easy as plugging each device into your computer’s USB ports. If you have a lot of equipment (mount, imaging camera, guide camera, focuser, filter wheel, etc), you should use an external powered-USB hub to make sure that all connected devices have adequate power. Once you have everything plugged in, run the following command to ensure that the system recognizes your equipment:
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
lsusb
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
You should see output similar to (but not the same as) the output here:
|
||||
|
||||
![][11]
|
||||
|
||||
You see in the output that the system recognizes the telescope mount (a SkyWatcher EQM-35 Pro) as _Prolific Technology, Inc. PL2303 Serial Port_, the imaging camera (a Sony a6000) as _Sony Corp. ILCE-6000_, and the guide camera (an Orion Starshoot, aka QHY5) as _Van Ouijen Technische Informatica_. Now that you have made sure your system recognizes your equipment, it’s time to open your desktop planetarium and telescope controller, KStars!
|
||||
You see in the output that the system recognizes the telescope mount (a SkyWatcher EQM-35 Pro) as *Prolific Technology, Inc. PL2303 Serial Port*, the imaging camera (a Sony a6000) as *Sony Corp. ILCE-6000*, and the guide camera (an Orion Starshoot, aka QHY5) as *Van Ouijen Technische Informatica*. Now that you have made sure your system recognizes your equipment, it’s time to open your desktop planetarium and telescope controller, KStars!
|
||||
|
||||
### Setting up KStars
|
||||
|
||||
@ -100,15 +80,15 @@ It’s time to open [KStars][12], which is a desktop planetarium and also includ
|
||||
|
||||
![][13]
|
||||
|
||||
Follow the prompts to choose your home location (where you will be imaging from) and _Download Extra Data…_
|
||||
Follow the prompts to choose your home location (where you will be imaging from) and *Download Extra Data…*
|
||||
|
||||
![Setting your location][14]
|
||||
![][14]
|
||||
|
||||
![“Download Extra Data”][15]
|
||||
![][15]
|
||||
|
||||
![Choosing which catalogs to download][16]
|
||||
![][16]
|
||||
|
||||
This will allow you to install additional star, nebula, and galaxy catalogs. You don’t need them, but they don’t take up too much space and add to the experience of using KStars. Once you’ve completed this, hit _Done_ in the bottom right corner to continue.
|
||||
This will allow you to install additional star, nebula, and galaxy catalogs. You don’t need them, but they don’t take up too much space and add to the experience of using KStars. Once you’ve completed this, hit *Done* in the bottom right corner to continue.
|
||||
|
||||
### Getting familiar with KStars
|
||||
|
||||
@ -116,49 +96,49 @@ Now is a good time to play around with the KStars interface. You are greeted wit
|
||||
|
||||
![][17]
|
||||
|
||||
This is the desktop planetarium which allows you to view the placement of objects in the night sky. Double-clicking an object selects it, and right clicking on an object gives you options like _Center & Track_ which will follow the object in the planetarium, compensating for [sidereal time][18]. _Show DSS Image_ shows a real [digitized sky survey][19] image of the selected object.
|
||||
This is the desktop planetarium which allows you to view the placement of objects in the night sky. Double-clicking an object selects it, and right clicking on an object gives you options like *Center & Track* which will follow the object in the planetarium, compensating for [sidereal time][18]. *Show DSS Image* shows a real [digitized sky survey][19] image of the selected object.
|
||||
|
||||
![][20]
|
||||
|
||||
Another essential feature is the _Set Time_ option in the toolbar. Clicking this will allow you to input a future (or past) time and then simulate the night sky as if that were the current date.
|
||||
Another essential feature is the *Set Time* option in the toolbar. Clicking this will allow you to input a future (or past) time and then simulate the night sky as if that were the current date.
|
||||
|
||||
![The Set Time button][21]
|
||||
![][21]
|
||||
|
||||
### Configuring capture equipment with Ekos
|
||||
|
||||
You’re familiar with the KStars layout and some basic functions, so it’s time to move on configuring your equipment using the [Ekos][22] observatory controller and automation tool. To open Ekos, click the observatory button in the toolbar or go to _Tools_ > _Ekos_.
|
||||
You’re familiar with the KStars layout and some basic functions, so it’s time to move on configuring your equipment using the [Ekos][22] observatory controller and automation tool. To open Ekos, click the observatory button in the toolbar or go to *Tools* > *Ekos*.
|
||||
|
||||
![The Ekos button on the toolbar][23]
|
||||
![][23]
|
||||
|
||||
You will see another setup wizard: the _Ekos Profile Wizard_. Click _Next_ to start the wizard.
|
||||
You will see another setup wizard: the *Ekos Profile Wizard*. Click *Next* to start the wizard.
|
||||
|
||||
![][24]
|
||||
|
||||
In this tutorial, you have all of our equipment connected directly to your computer. A future article we will cover using an INDI server installed on a remote computer to control our equipment, allowing you to connect over a network and not have to be in the same physical space as your gear. For now though, select _Equipment is attached to this device_.
|
||||
In this tutorial, you have all of our equipment connected directly to your computer. A future article we will cover using an INDI server installed on a remote computer to control our equipment, allowing you to connect over a network and not have to be in the same physical space as your gear. For now though, select *Equipment is attached to this device*.
|
||||
|
||||
![][25]
|
||||
|
||||
You are now asked to name your equipment profile. I usually name mine something like “Local Gear” to differentiate between profiles that are for remote gear, but name your profile what you wish. We will leave the button marked _Internal Guide_ checked and won’t select any additional services. Now click the _Create Profile & Select Devices_ button.
|
||||
You are now asked to name your equipment profile. I usually name mine something like “Local Gear” to differentiate between profiles that are for remote gear, but name your profile what you wish. We will leave the button marked *Internal Guide* checked and won’t select any additional services. Now click the *Create Profile & Select Devices* button.
|
||||
|
||||
![][26]
|
||||
|
||||
This next screen is where we can select your particular driver to use for each individual piece of equipment. This part will be specific to your setup depending on what gear you use. For this tutorial, I will select the drivers for my setup.
|
||||
|
||||
My mount, a [SkyWatcher EQM-35 Pro][27], uses the _EQMod Mount_ under _SkyWatcher_ in the menu (this driver is also compatible with all SkyWatcher equatorial mounts, including the [EQ6-R Pro][28] and the [EQ8-R Pro][29]). For my Sony a6000 imaging camera, I choose the _Sony DSLR_ under _DSLRs_ under the CCD category. Under _Guider_, I choose the _QHY CCD_ under _QHY_ for my Orion Starshoot (and any QHY5 series camera). That last driver we want to select will be under the Aux 1 category. We want to select _Astrometry_ from the drop-down window. This will enable the Astrometry plate-solver from within Ekos that will allow our telescope to automatically figure out where in the night sky it is pointed, saving us the time and hassle of doing a one, two, or three star calibration after setting up our mount.
|
||||
My mount, a [SkyWatcher EQM-35 Pro][27], uses the *EQMod Mount* under *SkyWatcher* in the menu (this driver is also compatible with all SkyWatcher equatorial mounts, including the [EQ6-R Pro][28] and the [EQ8-R Pro][29]). For my Sony a6000 imaging camera, I choose the *Sony DSLR* under *DSLRs* under the CCD category. Under *Guider*, I choose the *QHY CCD* under *QHY* for my Orion Starshoot (and any QHY5 series camera). That last driver we want to select will be under the Aux 1 category. We want to select *Astrometry* from the drop-down window. This will enable the Astrometry plate-solver from within Ekos that will allow our telescope to automatically figure out where in the night sky it is pointed, saving us the time and hassle of doing a one, two, or three star calibration after setting up our mount.
|
||||
|
||||
You selected your drivers. Now it’s time to configure your telescope. Add new telescope profiles by clicking on the + button in the lower right. This is essential for computing field-of-view measurements so you can tell what your images will look like when you open the shutter. Once you click the + button, you will be presented with a form where you can enter the specifications of your telescope and guide scope. For my imaging telescope, I will enter Celestron into the _Vendor_ field, SS-80 into the _Model_ field, I will leave the _Driver_ field as None, _Type_ field as Refractor, _Aperture_ as 80mm, and _Focal Length_ as 400mm.
|
||||
You selected your drivers. Now it’s time to configure your telescope. Add new telescope profiles by clicking on the + button in the lower right. This is essential for computing field-of-view measurements so you can tell what your images will look like when you open the shutter. Once you click the + button, you will be presented with a form where you can enter the specifications of your telescope and guide scope. For my imaging telescope, I will enter Celestron into the *Vendor* field, SS-80 into the *Model* field, I will leave the *Driver* field as None, *Type* field as Refractor, *Aperture* as 80mm, and *Focal Length* as 400mm.
|
||||
|
||||
![][30]
|
||||
|
||||
After you enter the data, hit the _Save_ button. You will see the data you just entered appear in the left window with an index number of 1 next to it. Now you can go about entering the specs for your guide scope following the steps above. Once you hit save here, the guide scope will also appear in the left window with an index number of 2. Once all of your scopes are entered, close this window. Now select your _Primary_ and _Guide_ telescopes from the drop-down window.
|
||||
After you enter the data, hit the *Save* button. You will see the data you just entered appear in the left window with an index number of 1 next to it. Now you can go about entering the specs for your guide scope following the steps above. Once you hit save here, the guide scope will also appear in the left window with an index number of 2. Once all of your scopes are entered, close this window. Now select your *Primary* and *Guide* telescopes from the drop-down window.
|
||||
|
||||
![][31]
|
||||
|
||||
After all that work, everything should be correctly configured! Click the _Close_ button and complete the final bit of setup.
|
||||
After all that work, everything should be correctly configured! Click the *Close* button and complete the final bit of setup.
|
||||
|
||||
### Starting your capture equipment
|
||||
|
||||
This last step before you can start taking images should be easy enough. Click the _Play_ button under Start & Stop Ekos to connect to your equipment.
|
||||
This last step before you can start taking images should be easy enough. Click the *Play* button under Start & Stop Ekos to connect to your equipment.
|
||||
|
||||
![][32]
|
||||
|
||||
@ -166,26 +146,23 @@ You will be greeted with a screen that looks similar to this:
|
||||
|
||||
![][33]
|
||||
|
||||
When you click on the tabs at the top of the screen, they should all show a green dot next to _Connection_, indicating that they are connected to your system. On my setup, the baud rate for my mount (the EQMod Mount tab) is set incorrectly, and so the mount is not connected.
|
||||
When you click on the tabs at the top of the screen, they should all show a green dot next to *Connection*, indicating that they are connected to your system. On my setup, the baud rate for my mount (the EQMod Mount tab) is set incorrectly, and so the mount is not connected.
|
||||
|
||||
![][34]
|
||||
|
||||
This is an easy fix; click on the _EQMod Mount_ tab, then the _Connection_ sub-tab, and then change the baud rate from 9600 to 115200. Now is a good time to ensure the serial port under _Ports_ is the correct serial port for your mount. You can check which port the system has mounted your device on by running the command:
|
||||
```
|
||||
This is an easy fix; click on the *EQMod Mount* tab, then the *Connection* sub-tab, and then change the baud rate from 9600 to 115200. Now is a good time to ensure the serial port under *Ports* is the correct serial port for your mount. You can check which port the system has mounted your device on by running the command:
|
||||
|
||||
```
|
||||
|
||||
ls /dev
|
||||
|
||||
```
|
||||
| grep USB
|
||||
```
|
||||
|
||||
You should see _ttyUSB0_. If there is more than one USB-serial device plugged in at a time, you will see more than one ttyUSB port, with an incrementing following number. To figure out which port is correct. unplug your mount and run the command again.
|
||||
You should see *ttyUSB0*. If there is more than one USB-serial device plugged in at a time, you will see more than one ttyUSB port, with an incrementing following number. To figure out which port is correct. unplug your mount and run the command again.
|
||||
|
||||
Now click on the _Main Control_ sub-tab, click _Connect_ again, and wait for the mount to connect. It might take a few seconds, be patient and it should connect.
|
||||
Now click on the *Main Control* sub-tab, click *Connect* again, and wait for the mount to connect. It might take a few seconds, be patient and it should connect.
|
||||
|
||||
The last thing to do is set the sensor and pixel size parameters for my camera. Under the _Sony DSLR Alpha-A6000 (Control)_ tab, select the _Image Info_ sub-tab. This is where you can enter your sensor specifications; if you don’t know them, a quick search on the internet will bring you your sensor’s maximum resolution as well as pixel pitch. Enter this data into the right-side boxes, then press the _Set_ button to load them into the left boxes and save them into memory. Hit the _Close_ button when you are done.
|
||||
The last thing to do is set the sensor and pixel size parameters for my camera. Under the *Sony DSLR Alpha-A6000 (Control)* tab, select the *Image Info* sub-tab. This is where you can enter your sensor specifications; if you don’t know them, a quick search on the internet will bring you your sensor’s maximum resolution as well as pixel pitch. Enter this data into the right-side boxes, then press the *Set* button to load them into the left boxes and save them into memory. Hit the *Close* button when you are done.
|
||||
|
||||
![][35]
|
||||
|
||||
@ -198,14 +175,14 @@ Your equipment is ready to use. In the next article, you will learn how to captu
|
||||
via: https://fedoramagazine.org/astrophotography-with-fedora-astronomy-lab-setting-up/
|
||||
|
||||
作者:[Geoffrey Marr][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/coremodule/
|
||||
[b]: https://github.com/lujun9972
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2021/02/astrophotography-setup-2-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/wp-content/uploads/2020/11/IMG_4151-768x1024.jpg
|
||||
[3]: https://labs.fedoraproject.org/en/astronomy/
|
||||
|
@ -1,15 +1,16 @@
|
||||
[#]: subject: (Navigate your FreeDOS system)
|
||||
[#]: via: (https://opensource.com/article/21/2/freedos-dir)
|
||||
[#]: author: (Kevin O'Brien https://opensource.com/users/ahuka)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Navigate your FreeDOS system"
|
||||
[#]: via: "https://opensource.com/article/21/2/freedos-dir"
|
||||
[#]: author: "Kevin O'Brien https://opensource.com/users/ahuka"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Navigate your FreeDOS system
|
||||
======
|
||||
Master the DIR command to navigate your way around FreeDOS.
|
||||
|
||||
![A map with a route highlighted][1]
|
||||
|
||||
[FreeDOS][2] is an open source implementation of DOS. It's not a remix of Linux, and it is compatible with the operating system that introduced many people to personal computing. This makes it an important resource for running legacy applications, playing retro games, updating firmware on motherboards, and experiencing a little bit of living computer history. In this article, I'll look at some of the essential commands used to navigate a FreeDOS system.
|
||||
@ -22,7 +23,6 @@ There are many reasons not to work exclusively in your root directory. First of
|
||||
|
||||
The FreeDOS `CD` command changes your current working subdirectory to another subdirectory. Imagine a computer with the following directory structure:
|
||||
|
||||
|
||||
```
|
||||
C:\
|
||||
\LETTERS\
|
||||
@ -34,48 +34,43 @@ C:\
|
||||
\SCHOOL\
|
||||
```
|
||||
|
||||
You start in the `C:\` directory, so to navigate to your love letter directory, you can use `CD`:
|
||||
|
||||
You start in the `C:\` directory, so to navigate to your love letter directory, you can use `CD` :
|
||||
|
||||
```
|
||||
`C:\>CD \LETTERS\LOVE\`
|
||||
C:\>CD \LETTERS\LOVE\
|
||||
```
|
||||
|
||||
To navigate to your `\LETTERS\BUSINESS` directory, you must specify the path to your business letters from a common fixed point on your filesystem. The most reliable starting location is `C:\`, because it's where _everything_ on your computer is stored.
|
||||
|
||||
To navigate to your `\LETTERS\BUSINESS` directory, you must specify the path to your business letters from a common fixed point on your filesystem. The most reliable starting location is `C:\`, because it's where *everything* on your computer is stored.
|
||||
|
||||
```
|
||||
`C:\LETTERS\LOVE\>CD C:\LETTERS\BUSINESS`
|
||||
C:\LETTERS\LOVE\>CD C:\LETTERS\BUSINESS
|
||||
```
|
||||
|
||||
#### Navigating with dots
|
||||
|
||||
There's a useful shortcut for navigating your FreeDOS system, which takes the form of dots. Two dots (`..`) tell FreeDOS you want to move "back" or "down" in your directory tree. For instance, the `LETTERS` directory in this example system contains one subdirectory called `LOVE` and another called `BUSINESS`. If you're in `LOVE` currently, and you want to step back and change over to `BUSINESS`, you can just use two dots to represent that move:
|
||||
|
||||
There's a useful shortcut for navigating your FreeDOS system, which takes the form of dots. Two dots (`..` ) tell FreeDOS you want to move "back" or "down" in your directory tree. For instance, the `LETTERS` directory in this example system contains one subdirectory called `LOVE` and another called `BUSINESS`. If you're in `LOVE` currently, and you want to step back and change over to `BUSINESS`, you can just use two dots to represent that move:
|
||||
|
||||
```
|
||||
C:\LETTERS\LOVE\>CD ..\BUSINESS
|
||||
C:\LETTERS\BUSINESS\>
|
||||
C:\LETTERS\LOVE\>CD ..\BUSINESS
|
||||
C:\LETTERS\BUSINESS\>
|
||||
```
|
||||
|
||||
To get all the way back to your root directory, just use the right number of dots:
|
||||
|
||||
|
||||
```
|
||||
C:\LETTERS\BUSINESS\: CD ..\\..
|
||||
C:\>
|
||||
C:\LETTERS\BUSINESS\: CD ..\..
|
||||
C:\>
|
||||
```
|
||||
|
||||
#### Navigational shortcuts
|
||||
|
||||
There are some shortcuts for navigating directories, too.
|
||||
There are some shortcuts for navigating directories, too.
|
||||
|
||||
To get back to the root directory from wherever you are:
|
||||
|
||||
|
||||
```
|
||||
C:\LETTERS\BUSINESS\>CD \
|
||||
C:\>
|
||||
C:\LETTERS\BUSINESS\>CD \
|
||||
C:\>
|
||||
```
|
||||
|
||||
### List directory contents with DIR
|
||||
@ -84,9 +79,8 @@ The `DIR` command displays the contents of a subdirectory, but it can also funct
|
||||
|
||||
`DIR` displays the contents of the current working subdirectory, and with an optional path argument, it displays the contents of some other subdirectory:
|
||||
|
||||
|
||||
```
|
||||
C:\LETTERS\BUSINESS\>DIR
|
||||
C:\LETTERS\BUSINESS\>DIR
|
||||
MTG_CARD TXT 1344 12-29-2020 3:06p
|
||||
NON TXT 381 12-31-2020 8:12p
|
||||
SOMUCHFO TXT 889 12-31-2020 9:36p
|
||||
@ -97,49 +91,49 @@ TEST BAT 32 01-03-2021 10:34a
|
||||
|
||||
With a special attribute argument, you can use `DIR` to find and filter out certain kinds of files. There are 10 attributes you can specify:
|
||||
|
||||
`H` | Hidden
|
||||
---|---
|
||||
`-H` | Not hidden
|
||||
`S` | System
|
||||
`-S` | Not system
|
||||
`A` | Archivable files
|
||||
`-A` | Already archived files
|
||||
`R` | Read-only files
|
||||
`-R` | Not read-only (i.e., editable and deletable) files
|
||||
`D` | Directories only, no files
|
||||
`-D` | Files only, no directories
|
||||
| - | - |
|
||||
| :- | :- |
|
||||
| H | Hidden |
|
||||
| -H | Not hidden |
|
||||
| S | System |
|
||||
| -S | Not system |
|
||||
| A | Archivable files |
|
||||
| -A | Already archived files |
|
||||
| R | Read-only files |
|
||||
| -R | Not read-only (i.e., editable and deletable) files |
|
||||
| D | Directories only, no files |
|
||||
| -D | Files only, no directories |
|
||||
|
||||
These special designators are denoted with `/A:` followed by the attribute letter. You can enter as many attributes as you like, in order, without leaving a space between them. For instance, to view only hidden directories:
|
||||
|
||||
|
||||
```
|
||||
C:\MEMOS\>DIR /A:HD
|
||||
.OBSCURE <DIR> 01-08-2021 10:10p
|
||||
C:\MEMOS\>DIR /A:HD
|
||||
.OBSCURE <DIR> 01-08-2021 10:10p
|
||||
```
|
||||
|
||||
#### Listing in order
|
||||
|
||||
You can also display the results of your `DIR` command in a specific order. The syntax for this is very similar to using attributes. You leave a space after the `DIR` command or after any other switches, and enter `/O:` followed by a selection. There are 12 possible selections:
|
||||
|
||||
`N` | Alphabetical order by file name
|
||||
---|---
|
||||
`-N` | Reverse alphabetical order by file name
|
||||
`E` | Alphabetical order by file extension
|
||||
`-E` | Reverse alphabetical order by file extension
|
||||
`D` | Order by date and time, earliest first
|
||||
`-D` | Order by date and time, latest first
|
||||
`S` | By size, increasing
|
||||
`-S` | By size, decreasing
|
||||
`C` | By [DoubleSpace][3] compression ratio, lowest to highest (version 6.0 only)
|
||||
`-C` | By DoubleSpace compression ratio, highest to lowest (version 6.0 only)
|
||||
`G` | Group directories before other files
|
||||
`-G` | Group directories after other files
|
||||
| - | - |
|
||||
| :- | :- |
|
||||
| N | Alphabetical order by file name |
|
||||
| -N | Reverse alphabetical order by file name |
|
||||
| E | Alphabetical order by file extension |
|
||||
| -E | Reverse alphabetical order by file extension |
|
||||
| D | Order by date and time, earliest first |
|
||||
| -D | Order by date and time, latest first |
|
||||
| S | By size, increasing |
|
||||
| -S | By size, decreasing |
|
||||
| C | By DoubleSpace compression ratio, lowest to highest (version 6.0 only) |
|
||||
| -C | By DoubleSpace compression ratio, highest to lowest (version 6.0 only) |
|
||||
| G | Group directories before other files |
|
||||
| -G | Group directories after other files |
|
||||
|
||||
To see your directory listing grouped by file extension:
|
||||
|
||||
|
||||
```
|
||||
C:\>DIR /O:E
|
||||
C:\>DIR /O:E
|
||||
TEST BAT 01-10-2021 7:11a
|
||||
TIMER EXE 01-11-2021 6:06a
|
||||
AAA TXT 01-09-2021 4:27p
|
||||
@ -149,9 +143,8 @@ This returns a list of files in alphabetical order of file extension.
|
||||
|
||||
If you're looking for a file you were working on yesterday, you can order by modification time:
|
||||
|
||||
|
||||
```
|
||||
C:\>DIR /O:-D
|
||||
C:\>DIR /O:-D
|
||||
AAA TXT 01-09-2021 4:27p
|
||||
TEST BAT 01-10-2021 7:11a
|
||||
TIMER EXE 01-11-2021 6:06a
|
||||
@ -163,12 +156,11 @@ If you need to clean up your hard drive because you're running out of space, you
|
||||
|
||||
You can use multiple arguments in a `DIR` command to achieve fairly complex results. Remember that each argument has to be separated from its neighbors by a blank space on each side:
|
||||
|
||||
|
||||
```
|
||||
`C:\>DIR /A:A /O:D /P`
|
||||
C:\>DIR /A:A /O:D /P
|
||||
```
|
||||
|
||||
This command selects only those files that have not yet been backed up (`/A:A`), orders them by date, beginning with the oldest (`/O:D`), and displays the results on your monitor one page at a time (`/P`). So you can really do some slick stuff with the `DIR` command once you've mastered these arguments and switches.
|
||||
This command selects only those files that have not yet been backed up (`/A:A` ), orders them by date, beginning with the oldest (`/O:D` ), and displays the results on your monitor one page at a time (`/P` ). So you can really do some slick stuff with the `DIR` command once you've mastered these arguments and switches.
|
||||
|
||||
### Terminology
|
||||
|
||||
@ -180,24 +172,21 @@ If it has a slash in front, it is a switch. So all switches are also arguments,
|
||||
|
||||
FreeDOS can be very different from what you're used to if you're used to Windows or macOS, and it can be just different enough if you're used to Linux. A little practice goes a long way, though, so try some of these on your own. You can always get a help message with the `/?` switch. The best way to get comfortable with these commands is to practice using them.
|
||||
|
||||
* * *
|
||||
|
||||
_Some of the information in this article was previously published in [DOS lesson 12: Expert DIR use][4] (CC BY-SA 4.0)._
|
||||
*Some of the information in this article was previously published in [DOS lesson 12: Expert DIR use][3] (CC BY-SA 4.0).*
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/2/freedos-dir
|
||||
|
||||
作者:[Kevin O'Brien][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ahuka
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/map_route_location_gps_path.png?itok=RwtS4DsU (A map with a route highlighted)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/map_route_location_gps_path.png
|
||||
[2]: https://www.freedos.org/
|
||||
[3]: https://en.wikipedia.org/wiki/DriveSpace
|
||||
[4]: https://www.ahuka.com/dos-lessons-for-self-study-purposes/dos-lesson-12-expert-dir-use/
|
||||
[3]: https://www.ahuka.com/dos-lessons-for-self-study-purposes/dos-lesson-12-expert-dir-use/
|
||||
|
@ -1,17 +1,19 @@
|
||||
[#]: subject: (5 tips for choosing an Ansible collection that's right for you)
|
||||
[#]: via: (https://opensource.com/article/21/3/ansible-collections)
|
||||
[#]: author: (Tadej Borovšak https://opensource.com/users/tadeboro)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "5 tips for choosing an Ansible collection that's right for you"
|
||||
[#]: via: "https://opensource.com/article/21/3/ansible-collections"
|
||||
[#]: author: "Tadej Borovšak https://opensource.com/users/tadeboro"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
5 tips for choosing an Ansible collection that's right for you
|
||||
======
|
||||
Try these strategies to find and vet collections of Ansible plugins and
|
||||
modules before you install them.
|
||||
![Woman sitting in front of her computer][1]
|
||||
Try these strategies to find and vet collections of Ansible plugins and modules before you install them.
|
||||
|
||||
![Women in computing and open source][1]
|
||||
|
||||
Image by: Ray Smith
|
||||
|
||||
In August 2020, Ansible issued its first release since the developers split the core functionality from the vast majority of its modules and plugins. A few [basic Ansible modules][2] remain part of core Ansible—modules for templating configuration files, managing services, and installing packages. All the other modules and plugins found their homes in dedicated [Ansible collections][3].
|
||||
|
||||
@ -25,7 +27,6 @@ With the introduction of Ansible collections, [Ansible Galaxy][7] became the cen
|
||||
|
||||
Ansible comes bundled with the `ansible-galaxy` tool for installing collections. Once you know what Ansible collection you want to install, things are relatively straightforward: Run the installation command listed on the Ansible Galaxy page. Ansible takes care of downloading and installing it. For example:
|
||||
|
||||
|
||||
```
|
||||
$ ansible-galaxy collection install sensu.sensu_go
|
||||
Process install dependency map
|
||||
@ -44,7 +45,7 @@ The ability to install Ansible collections offered a lot more control over the c
|
||||
|
||||
Now users are solely responsible for the quality of content they use to build Ansible playbooks. But how can you separate high-quality content from the rest? Here are five things to check when evaluating an Ansible collection.
|
||||
|
||||
#### 1\. Documentation
|
||||
#### 1. Documentation
|
||||
|
||||
Once you find a potential candidate on Ansible Galaxy, check its documentation first. In an ideal world, each Ansible collection would have a dedicated documentation site. For example, the [Sensu Go][8] and [F5 Networks][9] Ansible collections have them. Most other Ansible collections come only with a README file, but this will change for the better once the documentation tools mature.
|
||||
|
||||
@ -52,10 +53,9 @@ The Ansible collection's documentation should contain at least a quickstart tuto
|
||||
|
||||
Another essential part of the documentation is a detailed module, plugin, and role reference guide. Collection authors do not always publish those guides on the internet, but they should always be accessible with the `ansible-doc` tool.
|
||||
|
||||
|
||||
```
|
||||
$ ansible-doc community.sops.sops_encrypt
|
||||
> SOPS_ENCRYPT (/home/tadej/.ansible/collections/ansible>
|
||||
> SOPS_ENCRYPT (/home/tadej/.ansible/collections/ansible>
|
||||
|
||||
Allows to encrypt binary data (Base64 encoded), text
|
||||
data, JSON or YAML data with sops.
|
||||
@ -63,7 +63,7 @@ $ ansible-doc community.sops.sops_encrypt
|
||||
* This module is maintained by The Ansible Community
|
||||
OPTIONS (= is mandatory):
|
||||
|
||||
\- attributes
|
||||
- attributes
|
||||
The attributes the resulting file or directory should
|
||||
have.
|
||||
To get supported flags look at the man page for
|
||||
@ -78,18 +78,17 @@ OPTIONS (= is mandatory):
|
||||
...
|
||||
```
|
||||
|
||||
#### 2\. Playbook readability
|
||||
#### 2. Playbook readability
|
||||
|
||||
An Ansible playbook should serve as a human-readable description of the desired state. To achieve that, modules from the Ansible collection under evaluation should have a consistent user interface and descriptive parameter names.
|
||||
|
||||
For example, if Ansible modules interact with a web service, authentication parameters should be separated from the rest. And all modules should use the same authentication parameters if possible.
|
||||
|
||||
|
||||
```
|
||||
\- name: Create a check that runs every 30 seconds
|
||||
- name: Create a check that runs every 30 seconds
|
||||
sensu.sensu_go.check:
|
||||
auth: &auth
|
||||
url: <https://my.sensu.host:8080>
|
||||
auth: &auth
|
||||
url: https://my.sensu.host:8080
|
||||
user: demo
|
||||
password: demo-pass
|
||||
name: check
|
||||
@ -97,36 +96,34 @@ For example, if Ansible modules interact with a web service, authentication para
|
||||
interval: 30
|
||||
publish: true
|
||||
|
||||
\- name: Create a filter
|
||||
- name: Create a filter
|
||||
sensu.sensu_go.filter:
|
||||
# Reuse the authentication data from before
|
||||
# Reuse the authentication data from before
|
||||
auth: *auth
|
||||
name: filter
|
||||
action: deny
|
||||
expressions:
|
||||
- event.check.interval == 10
|
||||
- event.check.interval == 10
|
||||
- event.check.occurrences == 1
|
||||
```
|
||||
|
||||
#### 3\. Basic functionality
|
||||
#### 3. Basic functionality
|
||||
|
||||
Before you start using third-party Ansible content in production, always check each Ansible module's basic functionality.
|
||||
|
||||
Probably the most critical property to look for is the result. Ansible modules and roles that enforce a state are much easier to use than their action-executing counterparts. This is because you can update your Ansible playbook and rerun it without risking a significant breakage.
|
||||
|
||||
|
||||
```
|
||||
\- name: Command module executes an action -> fails on re-run
|
||||
- name: Command module executes an action -> fails on re-run
|
||||
ansible.builtin.command: useradd demo
|
||||
|
||||
\- name: User module enforces a state -> safe to re-run
|
||||
- name: User module enforces a state -> safe to re-run
|
||||
ansible.builtin.user:
|
||||
name: demo
|
||||
```
|
||||
|
||||
You should also expect support for [check mode][12], which simulates the change without making it. If you combine check mode with state enforcement, you get a configuration drift detector for free.
|
||||
|
||||
|
||||
```
|
||||
$ ansible-playbook --check playbook.yaml
|
||||
|
||||
@ -142,11 +139,11 @@ host : ok=5 changed=2 unreachable=0 failed=0
|
||||
skipped=3 rescued=0 ignored=0
|
||||
```
|
||||
|
||||
#### 4\. Implementation robustness
|
||||
#### 4. Implementation robustness
|
||||
|
||||
A robustness check is a bit harder to perform if you've never developed an Ansible module or role before. Checking the continuous integration/continuous delivery (CI/CD) configuration files should give you a general idea of what is tested. Finding `ansible-test` and `molecule` commands in the test suite is an excellent sign.
|
||||
|
||||
#### 5\. Maintenance
|
||||
#### 5. Maintenance
|
||||
|
||||
During your evaluation, you should also take a look at the issue tracker and development activity. Finding old issues with no response from maintainers is one sign of a poorly maintained Ansible collection.
|
||||
|
||||
@ -163,15 +160,15 @@ If you are thinking about creating your own Ansible Collection, you can download
|
||||
via: https://opensource.com/article/21/3/ansible-collections
|
||||
|
||||
作者:[Tadej Borovšak][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/tadeboro
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/OSDC_women_computing_3.png?itok=qw2A18BM (Woman sitting in front of her computer)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/OSDC_women_computing_3.png
|
||||
[2]: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/
|
||||
[3]: https://docs.ansible.com/ansible/latest/collections/index.html#list-of-collections
|
||||
[4]: https://galaxy.ansible.com/sensu/sensu_go
|
||||
|
@ -1,36 +1,34 @@
|
||||
[#]: subject: (Build a home thermostat with a Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/21/3/thermostat-raspberry-pi)
|
||||
[#]: author: (Joe Truncale https://opensource.com/users/jtruncale)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Build a home thermostat with a Raspberry Pi"
|
||||
[#]: via: "https://opensource.com/article/21/3/thermostat-raspberry-pi"
|
||||
[#]: author: "Joe Truncale https://opensource.com/users/jtruncale"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Build a home thermostat with a Raspberry Pi
|
||||
======
|
||||
The ThermOS project is an answer to the many downsides of off-the-shelf
|
||||
smart thermostats.
|
||||
The ThermOS project is an answer to the many downsides of off-the-shelf smart thermostats.
|
||||
|
||||
![Orange home vintage thermostat][1]
|
||||
|
||||
My wife and I moved into a new home in October 2020. As soon as it started getting cold, we realized some shortcomings of the home's older heating system (including one heating zone that was _always_ on). We had Nest thermostats in our previous home, and the current setup was not nearly as convenient. There are multiple thermostats in our house, and some had programmed heating schedules, others had different schedules, some had none at all.
|
||||
Image by: Photo by [Moja Msanii][2] on [Unsplash][3]
|
||||
|
||||
![Old thermostats][2]
|
||||
My wife and I moved into a new home in October 2020. As soon as it started getting cold, we realized some shortcomings of the home's older heating system (including one heating zone that was *always* on). We had Nest thermostats in our previous home, and the current setup was not nearly as convenient. There are multiple thermostats in our house, and some had programmed heating schedules, others had different schedules, some had none at all.
|
||||
|
||||
The home's previous owner left notes explaining how some of the thermostats worked. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![Old thermostats][4]
|
||||
|
||||
The home's previous owner left notes explaining how some of the thermostats worked. (Joseph Truncale, CC BY-SA 4.0)
|
||||
|
||||
It was time for a change, but the house has some constraints:
|
||||
|
||||
* It was built in the late 1960s with a renovation during the '90s.
|
||||
* The heat is hydronic (hot water baseboard).
|
||||
* It has six thermostats for the six heating zones.
|
||||
* There are only two wires that go to each thermostat for heat (red and white).
|
||||
* It was built in the late 1960s with a renovation during the '90s.
|
||||
* The heat is hydronic (hot water baseboard).
|
||||
* It has six thermostats for the six heating zones.
|
||||
* There are only two wires that go to each thermostat for heat (red and white).
|
||||
|
||||
|
||||
|
||||
![Furnace valves][4]
|
||||
|
||||
Taco (pronounced TAY-KO) zone valves at the furnace. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![Furnace valves][5]
|
||||
|
||||
### To buy or to build?
|
||||
|
||||
@ -38,36 +36,29 @@ I wanted "smart" thermostat control for all of the heat zones (schedules, automa
|
||||
|
||||
**Option 1: A Nest or Ecobee**
|
||||
|
||||
* It's expensive: No smart thermostat can handle multiple zones, so I would need one for each zone (~$200*6 = $1,200).
|
||||
* It's difficult: I would have to rerun the thermostat wire to get the infamous [C wire][5], which enables continuous power to the thermostat. The wires are 20 to 100 feet each, in-wall, and _might_ be stapled to the studs.
|
||||
* It's expensive: No smart thermostat can handle multiple zones, so I would need one for each zone (~$200*6 = $1,200).
|
||||
* It's difficult: I would have to rerun the thermostat wire to get the infamous [C wire][6], which enables continuous power to the thermostat. The wires are 20 to 100 feet each, in-wall, and might be stapled to the studs.
|
||||
|
||||
**Option 2: A battery-powered thermostat** such as the [Sensi WiFi thermostat][7]
|
||||
|
||||
* The batteries last only a month or two.
|
||||
* It's not HomeKit-compatible in battery-only mode.
|
||||
|
||||
**Option 2: A battery-powered thermostat** such as the [Sensi WiFi thermostat][6]
|
||||
**Option 3: A commercial-off-the-shelf thermostat**, but only one exists (kind of): [Honeywell's TrueZONE][8]
|
||||
|
||||
* The batteries last only a month or two.
|
||||
* It's not HomeKit-compatible in battery-only mode.
|
||||
* It's old and poorly supported (it was released in 2008).
|
||||
* It's expensive—more than $300 for just the controller, and you need a [RedLINK gateway][9] for a shoddy app to work.
|
||||
|
||||
|
||||
|
||||
**Option 3: A commercial-off-the-shelf thermostat**, but only one exists (kind of): [Honeywell's TrueZONE][7]
|
||||
|
||||
* It's old and poorly supported (it was released in 2008).
|
||||
* It's expensive—more than $300 for just the controller, and you need a [RedLINK gateway][8] for a shoddy app to work.
|
||||
|
||||
|
||||
|
||||
And the winner is…
|
||||
And the winner is…
|
||||
|
||||
**Option 4: Build my own!**
|
||||
I decided to build my own multizone smart thermostat, which I named [ThermOS][9].
|
||||
|
||||
* It's centralized at the furnace (you need one device, not six).
|
||||
* It uses the existing in-wall thermostat wires.
|
||||
* It's HomeKit compatible, complete with automation, scheduling, home/away, etc.
|
||||
* Anddddd it's… fun? Yeah, fun… I think.
|
||||
|
||||
I decided to build my own multizone smart thermostat, which I named [ThermOS][10].
|
||||
|
||||
* It's centralized at the furnace (you need one device, not six).
|
||||
* It uses the existing in-wall thermostat wires.
|
||||
* It's HomeKit compatible, complete with automation, scheduling, home/away, etc.
|
||||
* Anddddd it's… fun? Yeah, fun… I think.
|
||||
|
||||
### The ThermOS hardware
|
||||
|
||||
@ -75,79 +66,62 @@ I knew that I wanted to use a Raspberry Pi. Since they've gotten so inexpensive,
|
||||
|
||||
Here's a full list of the parts I used:
|
||||
|
||||
Name | Quantity | Price
|
||||
---|---|---
|
||||
Raspberry Pi 4 Model B 2GB | 1 | $29.99
|
||||
Raspberry Pi 4 official 15W power supply | 1 | $6.99
|
||||
Inland 400 tie-point breadboard | 1 | $2.99
|
||||
Inland 8 channel 5V relay module for Arduino | 1 | $8.99
|
||||
Inland DuPont jumper wire 20cm (3 pack) | 1 | $4.99
|
||||
DS18B20 temperature sensor (genuine) from Mouser.com | 6 | $6.00
|
||||
3-pin screw terminal blocks (40 pack) | 1 | $7.99
|
||||
RPi GPIO terminal block breakout board module for Raspberry Pi | 1 | $17.99
|
||||
Alligator clip test leads (10 pack) | 1 | $5.89
|
||||
Southwire 18/2 thermostat wire (50ft) | 1 | $10.89
|
||||
Shrinkwrap | 1 | $4.99
|
||||
Solderable breadboard (5 pack) | 1 | $11.99
|
||||
PCB mounting brackets (50 pack) | 1 | $7.99
|
||||
Plastic housing/enclosure | 1 | $27.92
|
||||
| Name | Quantity | Price |
|
||||
| :- | :- | :- |
|
||||
| Raspberry Pi 4 Model B 2GB | 1 | $29.99 |
|
||||
| Raspberry Pi 4 official 15W power supply | 1 | $6.99 |
|
||||
| Inland 400 tie-point breadboard | 1 | $2.99 |
|
||||
| Inland 8 channel 5V relay module for Arduino | 1 | $8.99 |
|
||||
| Inland DuPont jumper wire 20cm (3 pack) | 1 | $4.99 |
|
||||
| DS18B20 temperature sensor (genuine) from Mouser.com | 6 | $6.00 |
|
||||
| 3-pin screw terminal blocks (40 pack) | 1 | $7.99 |
|
||||
| RPi GPIO terminal block breakout board module for Raspberry Pi | 1 | $17.99 |
|
||||
| Alligator clip test leads (10 pack) | 1 | $5.89 |
|
||||
| Southwire 18/2 thermostat wire (50ft) | 1 | $10.89 |
|
||||
| Shrinkwrap | 1 | $4.99 |
|
||||
| Solderable breadboard (5 pack) | 1 | $11.99 |
|
||||
| PCB mounting brackets (50 pack) | 1 | $7.99 |
|
||||
| Plastic housing/enclosure | 1 | $27.92 |
|
||||
|
||||
I began drawing out the hardware diagram on [draw.io][10] and realized I lacked some crucial knowledge about the furnace. I opened the side panel and found the step-down transformer that takes the 120V electrical line and makes it 24V for the heating system. If your heating system is anything like mine, you'll see a lot of jumper wires between the Taco zone valves. Terminal 3 on the Taco is jumped across all of my zone valves. This is because it doesn't matter how many valves are on/open—it just controls the circulator pump. If any combination of one to five valves is open, it should be on; if no valves are open, it should be off… simple!
|
||||
I began drawing out the hardware diagram on [draw.io][11] and realized I lacked some crucial knowledge about the furnace. I opened the side panel and found the step-down transformer that takes the 120V electrical line and makes it 24V for the heating system. If your heating system is anything like mine, you'll see a lot of jumper wires between the Taco zone valves. Terminal 3 on the Taco is jumped across all of my zone valves. This is because it doesn't matter how many valves are on/open—it just controls the circulator pump. If any combination of one to five valves is open, it should be on; if no valves are open, it should be off… simple!
|
||||
|
||||
![Furnace wiring architecture][11]
|
||||
|
||||
ThermOS architecture using one zone. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![Furnace wiring architecture][12]
|
||||
|
||||
At its core, a thermostat is just a type of switch. Once the thermistor (temp sensor) inside the thermostat detects a lower temperature, the switch closes and completes the 24V circuit. Instead of having a thermostat in every room, this project keeps all of them right next to the furnace so that all six-zone valves can be controlled by a relay module using six of the eight relays. The Raspberry Pi acts as the brains of the thermostat and controls each relay independently.
|
||||
|
||||
![Manually setting relays using Raspberry Pi and Python][12]
|
||||
|
||||
Manually setting the relays using the Raspberry Pi and Python. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![Manually setting relays using Raspberry Pi and Python][13]
|
||||
|
||||
The next problem was how to get temperature readings from each room. I could have a wireless temperature sensor in each room running on an Arduino or Raspberry Pi, but that can get expensive and complicated. Instead, I wanted to reuse the existing thermostat wire in the walls but purely for temperature sensors.
|
||||
|
||||
The "1-wire" [DS18B20][13] temperature sensor appeared to fit the bill:
|
||||
The "1-wire" [DS18B20][14] temperature sensor appeared to fit the bill:
|
||||
|
||||
* It has an accuracy of +/- 0.5°C or 0.9°F.
|
||||
* It uses the "1-wire" protocol for data.
|
||||
* Most importantly, the DS18B20 can use "[parasitic power][14]" mode where it needs just two wires for power and data. Just a heads up… almost all of the DS18B20s out there are [counterfeit][15]. I purchased a few (hoping they were genuine), but they wouldn't work when I tried to use parasitic power. I then bought real ones from [Mouser.com][16], and they worked like a charm!
|
||||
* It has an accuracy of +/- 0.5°C or 0.9°F.
|
||||
* It uses the "1-wire" protocol for data.
|
||||
* Most importantly, the DS18B20 can use "[parasitic power][15]" mode where it needs just two wires for power and data. Just a heads up… almost all of the DS18B20s out there are [counterfeit][16]. I purchased a few (hoping they were genuine), but they wouldn't work when I tried to use parasitic power. I then bought real ones from [Mouser.com][17], and they worked like a charm!
|
||||
|
||||
![Temperature sensors][18]
|
||||
|
||||
Starting with a breadboard and all the components locally, I started writing code to interact with all of it. Once I proved out the concept, I added the existing in-wall thermostat wire into the mix. I got consistent readings with that setup, so I set out to make them a bit more polished. With help from my [dad][19], the self-proclaimed "just good enough" solderer, we soldered leads to the three-pin screw terminals (to avoid overheating the sensor) and then attached the sensor into the terminals. Now the sensors can be attached with wire nuts to the existing in-wall wiring.
|
||||
|
||||
![Temperature sensors][17]
|
||||
|
||||
Three DS18B20s connected using parasitic power on the same GPIO bus. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
|
||||
Starting with a breadboard and all the components locally, I started writing code to interact with all of it. Once I proved out the concept, I added the existing in-wall thermostat wire into the mix. I got consistent readings with that setup, so I set out to make them a bit more polished. With help from my [dad][18], the self-proclaimed "just good enough" solderer, we soldered leads to the three-pin screw terminals (to avoid overheating the sensor) and then attached the sensor into the terminals. Now the sensors can be attached with wire nuts to the existing in-wall wiring.
|
||||
|
||||
![Attaching temperature sensors][19]
|
||||
|
||||
The DS18B20s are attached to the old thermostat location using the existing wires. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![Attaching temperature sensors][20]
|
||||
|
||||
I'm still in the process of "prettifying" my temperature sensor wall mounts, but I've gone through a few 3D printing revisions, and I think I'm almost there.
|
||||
|
||||
![Wall mounts][20]
|
||||
|
||||
I started with a Nest-style mount and made my way to a flush-mount style. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![Wall mounts][21]
|
||||
|
||||
### The ThermOS software
|
||||
|
||||
As usual, writing the logic wasn't the hard part. However, deciding on the application architecture and framework was a confusing, multi-day process. I started out evaluating open source projects like [PiHome][21], but it relied on specific hardware _and_ was written in PHP. I'm a Python fan and decided to start from scratch and write my own REST API.
|
||||
As usual, writing the logic wasn't the hard part. However, deciding on the application architecture and framework was a confusing, multi-day process. I started out evaluating open source projects like [PiHome][22], but it relied on specific hardware *and* was written in PHP. I'm a Python fan and decided to start from scratch and write my own REST API.
|
||||
|
||||
Since HomeKit integration was so important, I figured I would eventually write a [HomeBridge][22] plugin to integrate it. I didn't realize that there was an entire Python HomeKit framework called [HAP-Python][23] that implements the accessory protocol. It helped me get a proof of concept running and controlled through my iPhone's Home app within 30 minutes.
|
||||
Since HomeKit integration was so important, I figured I would eventually write a [HomeBridge][23] plugin to integrate it. I didn't realize that there was an entire Python HomeKit framework called [HAP-Python][24] that implements the accessory protocol. It helped me get a proof of concept running and controlled through my iPhone's Home app within 30 minutes.
|
||||
|
||||
![ThermOS HomeKit integration][24]
|
||||
![ThermOS HomeKit integration][25]
|
||||
|
||||
Initial version of Apple HomeKit integration, with help from the HAP-Python framework. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
|
||||
![ThermOS software architecture][25]
|
||||
|
||||
ThermOS software architecture (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![ThermOS software architecture][26]
|
||||
|
||||
The rest of the "temp" logic is relatively straightforward, but I do want to highlight a piece that I initially missed. My code was running for a few days, and I was working on the hardware, when I noticed that my relays were turning on and off every few seconds. This "short-cycling" isn't necessarily harmful, but it certainly isn't efficient. To avoid that, I added some thresholding to make sure the heat toggles only when it's +/- 0.5C°.
|
||||
|
||||
Here is the threshold logic (you can see the [rubber-duck debugging][26] in the comments):
|
||||
|
||||
Here is the threshold logic (you can see the [rubber-duck debugging][27] in the comments):
|
||||
|
||||
```
|
||||
# check that we want heat
|
||||
@ -155,7 +129,7 @@ if self.target_state.value == 1:
|
||||
# if heat relay is already on, check if above threshold
|
||||
# if above, turn off .. if still below keep on
|
||||
if GPIO.input(self.relay_pin):
|
||||
if self.current_temp.value - self.target_temp.value >= 0.5:
|
||||
if self.current_temp.value - self.target_temp.value >= 0.5:
|
||||
status = 'HEAT ON - TEMP IS ABOVE TOP THRESHOLD, TURNING OFF'
|
||||
GPIO.output(self.relay_pin, GPIO.LOW)
|
||||
else:
|
||||
@ -163,96 +137,87 @@ if self.target_state.value == 1:
|
||||
GPIO.output(self.relay_pin, GPIO.HIGH)
|
||||
# if heat relay is not already on, check if below threshold
|
||||
elif not GPIO.input(self.relay_pin):
|
||||
if self.current_temp.value - self.target_temp.value <= -0.5:
|
||||
if self.current_temp.value - self.target_temp.value <= -0.5:
|
||||
status = 'HEAT OFF - TEMP IS BELOW BOTTOM THRESHOLD, TURNING ON'
|
||||
GPIO.output(self.relay_pin, GPIO.HIGH)
|
||||
else:
|
||||
status = 'HEAT OFF - KEEPING OFF'
|
||||
```
|
||||
|
||||
![Thresholding][27]
|
||||
|
||||
Thresholding allows longer stretches of time where the heat is off. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![Thresholding][28]
|
||||
|
||||
And I achieved my ultimate goal—to be able to control all of it from my phone.
|
||||
|
||||
![ThermOS as a HomeKit Hub][28]
|
||||
|
||||
ThermOS as a HomeKit Hub (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![ThermOS as a HomeKit Hub][29]
|
||||
|
||||
### Putting my ThermOS in a lunchbox
|
||||
|
||||
My proof of concept was pretty messy.
|
||||
|
||||
![Initial ThermOS setup][29]
|
||||
![Initial ThermOS setup][30]
|
||||
|
||||
ThermOS controlling a single zone (before packaging it) (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
|
||||
With the software and general hardware design in place, I started figuring out how to package all of the components in a more permanent and polished form. One of my main concerns for a permanent installation was to use a breadboard with DuPont jumper wires. I ordered some [solderable breadboards][30] and a [screw terminal breakout board][31] (thanks [@arduima][32] for the Raspberry Pi GPIO pins).
|
||||
With the software and general hardware design in place, I started figuring out how to package all of the components in a more permanent and polished form. One of my main concerns for a permanent installation was to use a breadboard with DuPont jumper wires. I ordered some [solderable breadboards][31] and a [screw terminal breakout board][32] (thanks [@arduima][33] for the Raspberry Pi GPIO pins).
|
||||
|
||||
Here's what the solderable breadboard with mounts and enclosure looked like in progress.
|
||||
|
||||
![ThermOS hardware being packaged][33]
|
||||
|
||||
Putting the ThermOS in a lunchbox. (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![ThermOS hardware][34]
|
||||
|
||||
And here it is, mounted in the boiler room.
|
||||
|
||||
![ThermOS mounted][34]
|
||||
|
||||
ThermOS mounted (Joseph Truncale, [CC BY-SA 4.0][3])
|
||||
![ThermOS mounted][35]
|
||||
|
||||
Now I just need to organize and label the wires, and then I can start swapping the remainder of the thermostats over to ThermOS. And I'll be on to my next project: ThermOS for my central air conditioning.
|
||||
|
||||
* * *
|
||||
Image by: (Joseph Truncale, CC BY-SA 4.0)
|
||||
|
||||
_This originally appeared on [Medium][35] and is republished with permission._
|
||||
*This originally appeared on [Medium][36] and is republished with permission.*
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/3/thermostat-raspberry-pi
|
||||
|
||||
作者:[Joe Truncale][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jtruncale
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/home-thermostat.jpg?itok=wuV1XL7t (Orange home vintage thermostat)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/oldthermostats.jpeg (Old thermostats)
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://opensource.com/sites/default/files/uploads/furnacevalves.jpeg (Furnace valves)
|
||||
[5]: https://smartthermostatguide.com/thermostat-c-wire-explained/
|
||||
[6]: https://www.amazon.com/Emerson-Thermostat-Version-Energy-Certified/dp/B01NB1OB0I
|
||||
[7]: https://www.honeywellhome.com/us/en/products/air/forced-air-zone-panels/truezone-hz432-panel-hz432-u/
|
||||
[8]: https://www.amazon.com/Honeywell-Redlink-Enabled-Internet-THM6000R7001/dp/B0783HK9ZZ
|
||||
[9]: https://github.com/truncj/thermos
|
||||
[10]: http://draw.io/
|
||||
[11]: https://opensource.com/sites/default/files/uploads/furnacewiring.png (Furnace wiring architecture)
|
||||
[12]: https://opensource.com/sites/default/files/uploads/settingrelays.gif (Manually setting relays using Raspberry Pi and Python)
|
||||
[13]: https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
|
||||
[14]: https://learn.openenergymonitor.org/electricity-monitoring/temperature/DS18B20-temperature-sensing
|
||||
[15]: https://github.com/cpetrich/counterfeit_DS18B20
|
||||
[16]: https://www.mouser.com/
|
||||
[17]: https://opensource.com/sites/default/files/uploads/tempsensors.png (Temperature sensors)
|
||||
[18]: https://twitter.com/jofredrick
|
||||
[19]: https://opensource.com/sites/default/files/uploads/attachingsensors.jpeg (Attaching temperature sensors)
|
||||
[20]: https://opensource.com/sites/default/files/uploads/wallmount.jpeg (Wall mounts)
|
||||
[21]: https://github.com/pihome-shc/pihome
|
||||
[22]: https://github.com/homebridge/homebridge
|
||||
[23]: https://github.com/ikalchev/HAP-python
|
||||
[24]: https://opensource.com/sites/default/files/uploads/iphoneintegration.gif (ThermOS HomeKit integration)
|
||||
[25]: https://opensource.com/sites/default/files/uploads/thermosarchitecture.png (ThermOS software architecture)
|
||||
[26]: https://en.wikipedia.org/wiki/Rubber_duck_debugging
|
||||
[27]: https://opensource.com/sites/default/files/uploads/thresholding.png (Thresholding)
|
||||
[28]: https://opensource.com/sites/default/files/uploads/thermoshomekit.png (ThermOS as a HomeKit Hub)
|
||||
[29]: https://opensource.com/sites/default/files/uploads/unpackaged.jpeg (Initial ThermOS setup)
|
||||
[30]: https://www.amazon.com/gp/product/B07ZV8FWM4/r
|
||||
[31]: https://www.amazon.com/gp/product/B084C69VSQ/
|
||||
[32]: https://twitter.com/dimitri_koshkin
|
||||
[33]: https://opensource.com/sites/default/files/uploads/breadboard.png (ThermOS hardware being packaged)
|
||||
[34]: https://opensource.com/sites/default/files/uploads/mounted.png (ThermOS mounted)
|
||||
[35]: https://joetruncale.medium.com/thermos-d089e1c4974b
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/home-thermostat.jpg
|
||||
[2]: https://unsplash.com/@mojamsanii?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[3]: https://unsplash.com/s/photos/thermostat?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[4]: https://opensource.com/sites/default/files/uploads/oldthermostats.jpeg
|
||||
[5]: https://opensource.com/sites/default/files/uploads/furnacevalves.jpeg
|
||||
[6]: https://smartthermostatguide.com/thermostat-c-wire-explained/
|
||||
[7]: https://www.amazon.com/Emerson-Thermostat-Version-Energy-Certified/dp/B01NB1OB0I
|
||||
[8]: https://www.honeywellhome.com/us/en/products/air/forced-air-zone-panels/truezone-hz432-panel-hz432-u/
|
||||
[9]: https://www.amazon.com/Honeywell-Redlink-Enabled-Internet-THM6000R7001/dp/B0783HK9ZZ
|
||||
[10]: https://github.com/truncj/thermos
|
||||
[11]: http://draw.io/
|
||||
[12]: https://opensource.com/sites/default/files/uploads/furnacewiring.png
|
||||
[13]: https://opensource.com/sites/default/files/uploads/settingrelays.gif
|
||||
[14]: https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
|
||||
[15]: https://learn.openenergymonitor.org/electricity-monitoring/temperature/DS18B20-temperature-sensing
|
||||
[16]: https://github.com/cpetrich/counterfeit_DS18B20
|
||||
[17]: https://www.mouser.com/
|
||||
[18]: https://opensource.com/sites/default/files/uploads/tempsensors.png
|
||||
[19]: https://twitter.com/jofredrick
|
||||
[20]: https://opensource.com/sites/default/files/uploads/attachingsensors.jpeg
|
||||
[21]: https://opensource.com/sites/default/files/uploads/wallmount.jpeg
|
||||
[22]: https://github.com/pihome-shc/pihome
|
||||
[23]: https://github.com/homebridge/homebridge
|
||||
[24]: https://github.com/ikalchev/HAP-python
|
||||
[25]: https://opensource.com/sites/default/files/uploads/iphoneintegration.gif
|
||||
[26]: https://opensource.com/sites/default/files/uploads/thermosarchitecture.png
|
||||
[27]: https://en.wikipedia.org/wiki/Rubber_duck_debugging
|
||||
[28]: https://opensource.com/sites/default/files/uploads/thresholding.png
|
||||
[29]: https://opensource.com/sites/default/files/uploads/thermoshomekit.png
|
||||
[30]: https://opensource.com/sites/default/files/uploads/unpackaged.jpeg
|
||||
[31]: https://www.amazon.com/gp/product/B07ZV8FWM4/r
|
||||
[32]: https://www.amazon.com/gp/product/B084C69VSQ/
|
||||
[33]: https://twitter.com/dimitri_koshkin
|
||||
[34]: https://opensource.com/sites/default/files/uploads/breadboard.png
|
||||
[35]: https://opensource.com/sites/default/files/uploads/mounted.png
|
||||
[36]: https://joetruncale.medium.com/thermos-d089e1c4974b
|
||||
|
@ -1,37 +1,35 @@
|
||||
[#]: subject: (Learn Java with object orientation by building a classic Breakout game)
|
||||
[#]: via: (https://opensource.com/article/21/3/java-object-orientation)
|
||||
[#]: author: (Vaneska Sousa https://opensource.com/users/vaneska)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Learn Java by building a classic arcade game"
|
||||
[#]: via: "https://opensource.com/article/21/3/java-object-orientation"
|
||||
[#]: author: "Vaneska Sousa https://opensource.com/users/vaneska"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Learn Java with object orientation by building a classic Breakout game
|
||||
Learn Java by building a classic arcade game
|
||||
======
|
||||
Practice how to structure a project and write Java code while having fun
|
||||
building a fun game.
|
||||
Practice how to structure a project and write Java code while having fun building a fun game.
|
||||
|
||||
![Learning and studying technology is the key to success][1]
|
||||
|
||||
As a second-semester student in systems and digital media at the Federal University of Ceará in Brazil, I was given the assignment to remake the classic Atari 2600 [Breakout game][2] from 1978. I am still in my infancy in learning software development, and this was a challenging experience. It was also a gainful one because I learned a lot, especially about applying object-oriented concepts.
|
||||
Image by: [WOCinTech Chat][2], [CC BY 2.0][3]
|
||||
|
||||
![Breakout game][3]
|
||||
As a second-semester student in systems and digital media at the Federal University of Ceará in Brazil, I was given the assignment to remake the classic Atari 2600 [Breakout game][4] from 1978. I am still in my infancy in learning software development, and this was a challenging experience. It was also a gainful one because I learned a lot, especially about applying object-oriented concepts.
|
||||
|
||||
(Vaneska Karen, [CC BY-SA 4.0][4])
|
||||
![Breakout game][5]
|
||||
|
||||
I'll explain how I accomplished this challenge, and if you follow the step-by-step instructions, at the end of this article, you will have the first pieces of your own classic Breakout game.
|
||||
|
||||
### Choosing Java and TotalCross
|
||||
|
||||
Several of my courses use [Processing][5], a software engine that uses [Java][6]. Java is a great language for learning programming concepts, in part because it's a strongly typed language.
|
||||
Several of my courses use [Processing][6], a software engine that uses [Java][7]. Java is a great language for learning programming concepts, in part because it's a strongly typed language.
|
||||
|
||||
Despite being free to choose any language or framework for my Breakout project, I chose to continue in Java to apply what I've learned in my coursework. I also wanted to use a framework so that I did not need to do everything from scratch. I considered using Godot, but that would mean I would hardly need to program at all.
|
||||
|
||||
Instead, I chose [TotalCross][7]. It is an open source software development kit (SDK) and framework with a simple game engine that generates code for [Linux Arm][8] devices (like the Raspberry Pi) and smartphones. Also, because I work for TotalCross, I have access to developers with much more experience than I have and know the platform very well. It seemed to be the safest way and, despite some strife, I don't regret it one bit. It was very cool to develop the whole project and see it running on the phone and the [Raspberry Pi][9].
|
||||
Instead, I chose [TotalCross][8]. It is an open source software development kit (SDK) and framework with a simple game engine that generates code for [Linux Arm][9] devices (like the Raspberry Pi) and smartphones. Also, because I work for TotalCross, I have access to developers with much more experience than I have and know the platform very well. It seemed to be the safest way and, despite some strife, I don't regret it one bit. It was very cool to develop the whole project and see it running on the phone and the [Raspberry Pi][10].
|
||||
|
||||
![Breakout remake][10]
|
||||
|
||||
Breakout remake built with Java and TotalCross running on Raspberry Pi 3 Model B. (Vaneska Karen, [CC BY-SA 4.0][4])
|
||||
![Breakout remake][11]
|
||||
|
||||
### Define the project mechanics and structure
|
||||
|
||||
@ -39,63 +37,52 @@ When starting to develop any application, and especially a game, you need to con
|
||||
|
||||
#### Game mechanics
|
||||
|
||||
1. The platform moves left or right, according to the user's command. When it reaches an end, it hits the "wall" (edge).
|
||||
2. When the ball hits the platform, it returns in the opposite direction it came from.
|
||||
3. Each time the ball hits a "brick" (blue, green, yellow, orange, or red), the brick disappears.
|
||||
4. When all the bricks in level 01 have been destroyed, new ones appear (in the same position as the previous one), and the ball's speed increases.
|
||||
5. When all the bricks in level 02 have been destroyed, the game continues without obstacles on the screen.
|
||||
6. The game ends when the ball falls.
|
||||
|
||||
|
||||
1. The platform moves left or right, according to the user's command. When it reaches an end, it hits the "wall" (edge).
|
||||
2. When the ball hits the platform, it returns in the opposite direction it came from.
|
||||
3. Each time the ball hits a "brick" (blue, green, yellow, orange, or red), the brick disappears.
|
||||
4. When all the bricks in level 01 have been destroyed, new ones appear (in the same position as the previous one), and the ball's speed increases.
|
||||
5. When all the bricks in level 02 have been destroyed, the game continues without obstacles on the screen.
|
||||
6. The game ends when the ball falls.
|
||||
|
||||
#### Project structure
|
||||
|
||||
* `RunBreakoutApplication.java` is the class responsible for calling the class that inherits the `GameEngine` and runs the simulator.
|
||||
* `Breakout.java` is the main class, which inherits from the `GameEngine` class and "assembles" the game, where it will call objects, define positions, etc.
|
||||
* The `sprites` package is where all the classes responsible for the sprites (e.g., the image and behavior of the blocks, platform, and ball) go.
|
||||
* The `util` packages contain classes used to facilitate project maintenance, such as constants, image initialization, and colors.
|
||||
|
||||
|
||||
* RunBreakoutApplication.java is the class responsible for calling the class that inherits the `GameEngine` and runs the simulator.
|
||||
* Breakout.java is the main class, which inherits from the `GameEngine` class and "assembles" the game, where it will call objects, define positions, etc.
|
||||
* The `sprites` package is where all the classes responsible for the sprites (e.g., the image and behavior of the blocks, platform, and ball) go.
|
||||
* The `util` packages contain classes used to facilitate project maintenance, such as constants, image initialization, and colors.
|
||||
|
||||
### Get hands-on with code
|
||||
|
||||
First, install the [TotalCross plugin from VSCode][11]. If you are using another [integrated development environment][12] (IDE), check TotalCross's documentation for installation instructions.
|
||||
|
||||
If you're using the plugin, just press `Ctrl`+`P`, type `totalcross`, and click `Create new project`. Fill in the requested information:
|
||||
|
||||
* `Folder name:` gameTC
|
||||
* `ArtifactId:` com.totalcross
|
||||
* `Project name:` Breakout
|
||||
* `TotalCross version:` 6.1.1 (or the most recent one)
|
||||
* `Build platforms:` -Android and -Linux_arm (select the platforms you want)
|
||||
First, install the [TotalCross plugin from VSCode][12]. If you are using another [integrated development environment][13] (IDE), check TotalCross's documentation for installation instructions.
|
||||
|
||||
If you're using the plugin, just press `Ctrl` +`P`, type `totalcross`, and click `Create new project`. Fill in the requested information:
|
||||
|
||||
* Folder name: gameTC
|
||||
* ArtifactId: com.totalcross
|
||||
* Project name: Breakout
|
||||
* TotalCross version: 6.1.1 (or the most recent one)
|
||||
* Build platforms: -Android and -Linux_arm (select the platforms you want)
|
||||
|
||||
When filling in the fields above and generating the project, if you are in the `RunBreakoutApplication.java` class, right-clicking on it and clicking "run" will open the simulator, and "Hello World!" will appear on your screen if you have created your Java project with TotalCross properly.
|
||||
|
||||
![HelloWorld project structure][13]
|
||||
![HelloWorld project structure][14]
|
||||
|
||||
(Vaneska Karen, [CC BY-SA 4.0][4])
|
||||
If you have a problem, check the [documentation][15] or ask the [TotalCross community][16] on Telegram for help.
|
||||
|
||||
If you have a problem, check the [documentation][14] or ask the [TotalCross community][15] on Telegram for help.
|
||||
|
||||
After the project is configured, the next step is to add the project's images in `Resources` > `Sprites`. Create two packages named `util` and `sprites` to work on later.
|
||||
After the project is configured, the next step is to add the project's images in `Resources` > `Sprites`. Create two packages named `util` and `sprites` to work on later.
|
||||
|
||||
The structure of your project will be:
|
||||
|
||||
![Project structure][16]
|
||||
|
||||
(Vaneska Karen, [CC BY-SA 4.0][4])
|
||||
![Project structure][17]
|
||||
|
||||
### Go behind the scenes
|
||||
|
||||
To make it easier to maintain the code and change the images to the colors you want to use, it's a good practice to [centralize everything by creating classes][17]. Place all of the classes for this function inside the `util` package.
|
||||
To make it easier to maintain the code and change the images to the colors you want to use, it's a good practice to [centralize everything by creating classes][18]. Place all of the classes for this function inside the `util` package.
|
||||
|
||||
#### Constants.java
|
||||
|
||||
First, create the `constants.java` class, which is where placement patterns (such as the edge between the screen and where the platform starts), speed, number of blocks, etc., reside. This is good for playing, changing numbers, and understanding where things change and why. It is a great exercise for those just starting with Java.
|
||||
|
||||
|
||||
```
|
||||
package com.totacross.util;
|
||||
|
||||
@ -105,15 +92,15 @@ import totalcross.util.UnitsConverter;
|
||||
|
||||
public class Constants {
|
||||
//Position
|
||||
public static final int BOTTOM_EDGE = UnitsConverter.toPixels(430 + [Control][18].DP);
|
||||
public static final int DP_23 = UnitsConverter.toPixels(23 + [Control][18].DP);
|
||||
public static final int DP_50 = UnitsConverter.toPixels(50 + [Control][18].DP);
|
||||
public static final int DP_100 = UnitsConverter.toPixels(100 + [Control][18].DP);
|
||||
public static final int BOTTOM_EDGE = UnitsConverter.toPixels(430 + Control.DP);
|
||||
public static final int DP_23 = UnitsConverter.toPixels(23 + Control.DP);
|
||||
public static final int DP_50 = UnitsConverter.toPixels(50 + Control.DP);
|
||||
public static final int DP_100 = UnitsConverter.toPixels(100 + Control.DP);
|
||||
|
||||
//Sprites
|
||||
public static final int EDGE_RACKET = UnitsConverter.toPixels(20 + [Control][18].DP);
|
||||
public static final int WIDTH_BALL = UnitsConverter.toPixels(15 + [Control][18].DP);
|
||||
public static final int HEIGHT_BALL = UnitsConverter.toPixels(15 + [Control][18].DP);
|
||||
public static final int EDGE_RACKET = UnitsConverter.toPixels(20 + Control.DP);
|
||||
public static final int WIDTH_BALL = UnitsConverter.toPixels(15 + Control.DP);
|
||||
public static final int HEIGHT_BALL = UnitsConverter.toPixels(15 + Control.DP);
|
||||
|
||||
//Bricks
|
||||
public static final int NUM_BRICKS = 10;
|
||||
@ -136,7 +123,6 @@ If you want to know more about the pixel density (DP) unit, I recommend reading
|
||||
|
||||
As the name suggests, this class is where you define the colors used in the game. I recommend naming things according to the color's purpose, such as background, font color, etc. This will make it easier to update your project's color palette in a single class.
|
||||
|
||||
|
||||
```
|
||||
package com.totacross.util;
|
||||
|
||||
@ -152,7 +138,6 @@ public class Colors {
|
||||
|
||||
The `images.java` class is undoubtedly the most frequently used.
|
||||
|
||||
|
||||
```
|
||||
package com.totacross.util;
|
||||
|
||||
@ -160,26 +145,27 @@ import static com.totacross.util.Constants.*;
|
||||
import totalcross.ui.dialog.MessageBox;
|
||||
import totalcross.ui.image.Image;
|
||||
|
||||
|
||||
public class Images {
|
||||
|
||||
public static [Image][20] paddle, ball;
|
||||
public static [Image][20] red, orange, dark_orange, yellow, green, blue;
|
||||
public static Image paddle, ball;
|
||||
public static Image red, orange, dark_orange, yellow, green, blue;
|
||||
|
||||
public static void loadImages() {
|
||||
try {
|
||||
// general
|
||||
paddle = new [Image][20]("sprites/paddle.png");
|
||||
ball = new [Image][20]("sprites/ball.png").getScaledInstance(WIDTH_BALL, HEIGHT_BALL);
|
||||
paddle = new Image("sprites/paddle.png");
|
||||
ball = new Image("sprites/ball.png").getScaledInstance(WIDTH_BALL, HEIGHT_BALL);
|
||||
|
||||
// Bricks
|
||||
red = new [Image][20]("sprites/red_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
orange = new [Image][20]("sprites/orange_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
dark_orange = new [Image][20]("sprites/orange2_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
yellow = new [Image][20]("sprites/yellow_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
green = new [Image][20]("sprites/green_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
blue = new [Image][20]("sprites/blue_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
red = new Image("sprites/red_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
orange = new Image("sprites/orange_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
dark_orange = new Image("sprites/orange2_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
yellow = new Image("sprites/yellow_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
green = new Image("sprites/green_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
blue = new Image("sprites/blue_brick.png").getScaledInstance(WIDTH_BRICKS, HEIGHT_BRICKS);
|
||||
|
||||
} catch ([Exception][21] e) {
|
||||
} catch (Exception e) {
|
||||
MessageBox.showException(e, true);
|
||||
}
|
||||
}
|
||||
@ -192,9 +178,7 @@ The `getScaledInstance()` method will manipulate the image to match the values p
|
||||
|
||||
At this point, your project should look like this:
|
||||
|
||||
![Project structure][22]
|
||||
|
||||
(Vaneska Karen, [CC BY-SA 4.0][4])
|
||||
![Project structure][20]
|
||||
|
||||
### Create your first sprite
|
||||
|
||||
@ -202,11 +186,10 @@ Now that the project is structured properly, you're ready to create your first c
|
||||
|
||||
#### Paddle.java
|
||||
|
||||
The `paddle.java` class must inherit from `sprite`, which is the class responsible for objects in games. This is a fundamental concept in game engine development, so when inheriting from sprites, the TotalCross framework will already be concerned with delimiting movement within the screen, detecting collisions between sprites, and other important functions. You can check all the details in [Javadoc][23].
|
||||
The `paddle.java` class must inherit from `sprite`, which is the class responsible for objects in games. This is a fundamental concept in game engine development, so when inheriting from sprites, the TotalCross framework will already be concerned with delimiting movement within the screen, detecting collisions between sprites, and other important functions. You can check all the details in [Javadoc][21].
|
||||
|
||||
In Breakout, the paddle moves on the X-axis at a speed determined by the user's command (by touch screen or mouse movement). The `paddle.java` class is responsible for defining this movement and the sprite's image (the "face"):
|
||||
|
||||
|
||||
```
|
||||
package com.totacross.sprites;
|
||||
|
||||
@ -218,7 +201,7 @@ import totalcross.ui.image.ImageException;
|
||||
public class Paddle extends Sprite {
|
||||
private static final int SPEED = 4;
|
||||
|
||||
public Paddle() throws [IllegalArgumentException][24], [IllegalStateException][25], ImageException {
|
||||
public Paddle() throws IllegalArgumentException, IllegalStateException, ImageException {
|
||||
super(Images.paddle, -1, true, null);
|
||||
}
|
||||
|
||||
@ -235,7 +218,7 @@ public class Paddle extends Sprite {
|
||||
}
|
||||
```
|
||||
|
||||
You indicate the image (`Images.paddle`) within the constructor, and the `move` method (a TotalCross feature) receives the speed defined at the beginning of the class. Experiment with other values and observe what happens with the movement.
|
||||
You indicate the image (`Images.paddle` ) within the constructor, and the `move` method (a TotalCross feature) receives the speed defined at the beginning of the class. Experiment with other values and observe what happens with the movement.
|
||||
|
||||
When the paddle is moving to the left, the center of the paddle at any moment is defined as itself minus the speed, and when it's moving to the right, it's itself plus the speed. Ultimately, you define the position of the sprite on the screen.
|
||||
|
||||
@ -247,17 +230,14 @@ When building your game engine, you need to focus on some standard points. For t
|
||||
|
||||
Basically, you will delete the automatically generated `initUI()` method and, instead of inheriting from `MainWindow`, you will inherit it from `GameEngine`. A "red" will appear in the name of your class, so just click on the lamp or the suggestion symbol for your IDE and click `Add unimplemented methods`. This will automatically generate the `onGameInit()` method, which is responsible for the moment when the game starts, i.e., the moment the `breakout` class is called.
|
||||
|
||||
Inside the constructor, you must add the style type (`MaterialUI`) and the refresh time on the screen (`70`), and signal that the game has an interface (`gameHasUI = true;`).
|
||||
Inside the constructor, you must add the style type (`MaterialUI` ) and the refresh time on the screen (`70` ), and signal that the game has an interface (`gameHasUI = true;` ).
|
||||
|
||||
Last but not least, you have to start the game through `this.start()` on `onGameInit()` and focus on some other methods:
|
||||
|
||||
* `onGameInit()` is the first method called. In it, you must initialize the sprites and images (`Images.loadImages`), and tell the game that it can start.
|
||||
* `onGameStart()`is called when the game starts. It sets the platform's initial position (in the center of the screen on the X-axis and below the center with a border on the Y-axis).
|
||||
* `onPaint()` is where you say what will be drawn for each frame. First, it paints the background black (to not leave traces of the sprites), then it displays the sprites with `.show()`.
|
||||
* The `onPenDrag` and `onPenDown` methods identify when the user moves the paddle (by dragging a finger on a touch screen or moving the mouse while pressing the left button). These methods change the paddle movement through the `setPos()` method, which triggers the `move` method in the `Paddle.java` class. Note that the last parameter of the `racket.setPos` method is `true` to precisely limit the paddle's movement within the screen so that it never disappears from the user's field of view.
|
||||
|
||||
|
||||
|
||||
* onGameInit() is the first method called. In it, you must initialize the sprites and images (Images.loadImages), and tell the game that it can start.
|
||||
* onGameStart()is called when the game starts. It sets the platform's initial position (in the center of the screen on the X-axis and below the center with a border on the Y-axis).
|
||||
* onPaint() is where you say what will be drawn for each frame. First, it paints the background black (to not leave traces of the sprites), then it displays the sprites with `.show()`.
|
||||
* The `onPenDrag` and `onPenDown` methods identify when the user `move`s the paddle (by dragging a finger on a touch screen or moving the mouse while pressing the left button). These methods change the paddle movement through the `setPos()` method, which triggers the move method in the `Paddle.java` class. Note that the last parameter of the `racket.setPos` method is `true` to precisely limit the paddle's movement within the screen so that it never disappears from the user's field of view.
|
||||
|
||||
```
|
||||
package com.totacross;
|
||||
@ -295,7 +275,7 @@ public class Breakout extends GameEngine {
|
||||
try {
|
||||
racket = new Paddle();
|
||||
|
||||
} catch ([Exception][21] e) {
|
||||
} catch (Exception e) {
|
||||
MessageBox.showException(e, true);
|
||||
MainWindow.exit(0);
|
||||
}
|
||||
@ -307,7 +287,7 @@ public class Breakout extends GameEngine {
|
||||
|
||||
//to draw the interface
|
||||
@Override
|
||||
public void onPaint([Graphics][26] g) {
|
||||
public void onPaint(Graphics g) {
|
||||
super.onPaint(g);
|
||||
if (gameIsRunning) {
|
||||
g.backColor = Colors.PRIMARY;
|
||||
@ -339,71 +319,64 @@ public class Breakout extends GameEngine {
|
||||
|
||||
To run the game, just click `RunBreakoutApplication.java` with the right mouse button, then click `run` to see how it looks.
|
||||
|
||||
![Breakout game remake on phone][27]
|
||||
|
||||
(Vaneska Karen, [CC BY-SA 4.0][4])
|
||||
![Breakout game remake][22]
|
||||
|
||||
If you want to run it on a Raspberry Pi, change the parameters in the `RunBreakoutApplication.java` class to:
|
||||
|
||||
|
||||
```
|
||||
` TotalCrossApplication.run(Breakout.class, "/scr", "848x480");`
|
||||
TotalCrossApplication.run(Breakout.class, "/scr", "848x480");
|
||||
```
|
||||
|
||||
This sets the screen size to match the Raspberry Pi.
|
||||
|
||||
![Breakout on Raspberry Pi][28]
|
||||
|
||||
(Vaneska Karen, [CC BY-SA 4.0][4])
|
||||
![Breakout on Raspberry Pi][23]
|
||||
|
||||
The first sprite and game mechanics are ready!
|
||||
|
||||
### Next steps
|
||||
|
||||
In the next article, I'll show how to add the ball sprite and make collisions. If you need help, call me in the [community group][15] on Telegram or post in the TotalCross [forum][29], where I'm available to help.
|
||||
In the next article, I'll show how to add the ball sprite and make collisions. If you need help, call me in the [community group][24] on Telegram or post in the TotalCross [forum][25], where I'm available to help.
|
||||
|
||||
If you put this article into practice, share your experience in the comments. All feedback is important! If you wish, favorite [TotalCross on GitHub][30], as it improves the project's relevance on the platform.
|
||||
If you put this article into practice, share your experience in the comments. All feedback is important! If you wish, favorite [TotalCross on GitHub][26], as it improves the project's relevance on the platform.
|
||||
|
||||
Image by: (Vaneska Karen, CC BY-SA 4.0)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/3/java-object-orientation
|
||||
|
||||
作者:[Vaneska Sousa][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/vaneska
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/studying-books-java-couch-education.png?itok=C9gasCXr (Learning and studying technology is the key to success)
|
||||
[2]: https://www.youtube.com/watch?v=Cr6z3AyhRr8
|
||||
[3]: https://opensource.com/sites/default/files/uploads/originalbreakout.gif (Breakout game)
|
||||
[4]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[5]: https://processing.org/
|
||||
[6]: https://opensource.com/resources/java
|
||||
[7]: https://opensource.com/article/20/7/totalcross-cross-platform-development
|
||||
[8]: https://www.arm.linux.org.uk/docs/whatis.php
|
||||
[9]: https://opensource.com/resources/raspberry-pi
|
||||
[10]: https://opensource.com/sites/default/files/uploads/breakoutremake.gif (Breakout remake)
|
||||
[11]: https://marketplace.visualstudio.com/items?itemName=totalcross.vscode-totalcross
|
||||
[12]: https://www.redhat.com/en/topics/middleware/what-is-ide
|
||||
[13]: https://opensource.com/sites/default/files/uploads/helloworld.png (HelloWorld project structure)
|
||||
[14]: https://learn.totalcross.com/
|
||||
[15]: https://t.me/guiforembedded
|
||||
[16]: https://opensource.com/sites/default/files/uploads/projectstructure.png (Project structure)
|
||||
[17]: https://learn.totalcross.com/documentation/guides/app-architecture/colors-fonts-and-images
|
||||
[18]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+control
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/studying-books-java-couch-education.png
|
||||
[2]: https://www.wocintechchat.com/
|
||||
[3]: https://creativecommons.org/licenses/by/2.0/
|
||||
[4]: https://www.youtube.com/watch?v=Cr6z3AyhRr8
|
||||
[5]: https://opensource.com/sites/default/files/uploads/originalbreakout.gif
|
||||
[6]: https://processing.org/
|
||||
[7]: https://opensource.com/resources/java
|
||||
[8]: https://opensource.com/article/20/7/totalcross-cross-platform-development
|
||||
[9]: https://www.arm.linux.org.uk/docs/whatis.php
|
||||
[10]: https://opensource.com/resources/raspberry-pi
|
||||
[11]: https://opensource.com/sites/default/files/uploads/breakoutremake.gif
|
||||
[12]: https://marketplace.visualstudio.com/items?itemName=totalcross.vscode-totalcross
|
||||
[13]: https://www.redhat.com/en/topics/middleware/what-is-ide
|
||||
[14]: https://opensource.com/sites/default/files/uploads/helloworld.png
|
||||
[15]: https://learn.totalcross.com/
|
||||
[16]: https://t.me/guiforembedded
|
||||
[17]: https://opensource.com/sites/default/files/uploads/projectstructure.png
|
||||
[18]: https://learn.totalcross.com/documentation/guides/app-architecture/colors-fonts-and-images
|
||||
[19]: https://material.io/design/layout/pixel-density.html
|
||||
[20]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+image
|
||||
[21]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+exception
|
||||
[22]: https://opensource.com/sites/default/files/uploads/projectstructure2.png (Project structure)
|
||||
[23]: https://en.wikipedia.org/wiki/Javadoc
|
||||
[24]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalargumentexception
|
||||
[25]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+illegalstateexception
|
||||
[26]: http://www.google.com/search?hl=en&q=allinurl%3Adocs.oracle.com+javase+docs+api+graphics
|
||||
[27]: https://opensource.com/sites/default/files/uploads/runbreakout.gif (Breakout game remake on phone)
|
||||
[28]: https://opensource.com/sites/default/files/uploads/runbreakout2.gif (Breakout on Raspberry Pi)
|
||||
[29]: http://forum.totalcross.com
|
||||
[30]: https://github.com/totalcross/totalcross
|
||||
[20]: https://opensource.com/sites/default/files/uploads/projectstructure2.png
|
||||
[21]: https://en.wikipedia.org/wiki/Javadoc
|
||||
[22]: https://opensource.com/sites/default/files/uploads/runbreakout.gif
|
||||
[23]: https://opensource.com/sites/default/files/uploads/runbreakout2.gif
|
||||
[24]: https://t.me/guiforembedded
|
||||
[25]: http://forum.totalcross.com
|
||||
[26]: https://github.com/totalcross/totalcross
|
@ -1,50 +1,44 @@
|
||||
[#]: subject: (Host your website with dynamic content and a database on a Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/21/3/web-hosting-raspberry-pi)
|
||||
[#]: author: (Marty Kalin https://opensource.com/users/mkalindepauledu)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Host your website with dynamic content and a database on a Raspberry Pi"
|
||||
[#]: via: "https://opensource.com/article/21/3/web-hosting-raspberry-pi"
|
||||
[#]: author: "Marty Kalin https://opensource.com/users/mkalindepauledu"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Host your website with dynamic content and a database on a Raspberry Pi
|
||||
======
|
||||
You can use free software to support a web application on a very
|
||||
lightweight computer.
|
||||
You can use free software to support a web application on a very lightweight computer.
|
||||
|
||||
![Digital creative of a browser on the internet][1]
|
||||
|
||||
Raspberry Pi's single-board machines have set the mark for cheap, real-world computing. With its model 4, the Raspberry Pi can host web applications with a production-grade web server, a transactional database system, and dynamic content through scripting. This article explains the installation and configuration details with a full code example. Welcome to web applications hosted on a very lightweight computer.
|
||||
|
||||
### The snowfall application
|
||||
|
||||
Imagine a downhill ski area large enough to have microclimates, which can mean dramatically different snowfalls across the area. The area is divided into regions, each of which has devices that record snowfall in centimeters; the recorded information then guides decisions on snowmaking, grooming, and other maintenance operations. The devices communicate, say, every 20 minutes with a server that updates a database that supports reports. Nowadays, the server-side software for such an application can be free _and_ production-grade.
|
||||
Imagine a downhill ski area large enough to have microclimates, which can mean dramatically different snowfalls across the area. The area is divided into regions, each of which has devices that record snowfall in centimeters; the recorded information then guides decisions on snowmaking, grooming, and other maintenance operations. The devices communicate, say, every 20 minutes with a server that updates a database that supports reports. Nowadays, the server-side software for such an application can be free *and* production-grade.
|
||||
|
||||
This snowfall application uses the following technologies:
|
||||
|
||||
* A [Raspberry Pi 4][2] running Debian
|
||||
* Nginx web server: The free version hosts over 400 million websites. This web server is easy to install, configure, and use.
|
||||
* [SQLite relational database system][3], which is file-based: A database, which can hold many tables, is a file on the local system. SQLite is lightweight but also [ACID-compliant][4]; it is suited for low to moderate volume. SQLite is likely the most widely used database system in the world, and the source code for SQLite is in the public domain. The current version is 3. A more powerful (but still free) option is PostgreSQL.
|
||||
* Python: The Python programming language can interact with databases such as SQLite and web servers such as Nginx. Python (version 3) comes with Linux and macOS systems.
|
||||
|
||||
|
||||
* A [Raspberry Pi 4][2] running Debian
|
||||
* Nginx web server: The free version hosts over 400 million websites. This web server is easy to install, configure, and use.
|
||||
* [SQLite relational database system][3], which is file-based: A database, which can hold many tables, is a file on the local system. SQLite is lightweight but also [ACID-compliant][4]; it is suited for low to moderate volume. SQLite is likely the most widely used database system in the world, and the source code for SQLite is in the public domain. The current version is 3. A more powerful (but still free) option is PostgreSQL.
|
||||
* Python: The Python programming language can interact with databases such as SQLite and web servers such as Nginx. Python (version 3) comes with Linux and macOS systems.
|
||||
|
||||
Python includes a software driver for communicating with SQLite. There are options for connecting Python scripts with Nginx and other web servers. One option is [uWSGI][5] (Web Server Gateway Interface), which updates the ancient CGI (Common Gateway Interface) from the 1990s.
|
||||
|
||||
Several factors speak for uWSGI:
|
||||
|
||||
* uWSGI is flexible. It can be used as either a lightweight concurrent web server or the backend application server connected to a web server such as Nginx.
|
||||
* Its setup is minimal.
|
||||
* The snowfall application involves a low to moderate volume of hits on the web server and database system. In general, CGI technologies are not fast by modern standards, but CGI performs well enough for department-level web applications such as this one.
|
||||
|
||||
|
||||
* uWSGI is flexible. It can be used as either a lightweight concurrent web server or the backend application server connected to a web server such as Nginx.
|
||||
* Its setup is minimal.
|
||||
* The snowfall application involves a low to moderate volume of hits on the web server and database system. In general, CGI technologies are not fast by modern standards, but CGI performs well enough for department-level web applications such as this one.
|
||||
|
||||
Various acronyms describe the uWSGI option. Here's a sketch of the three principal ones:
|
||||
|
||||
* **WSGI** is a Python specification for an interface between a web server on one side, and an application or an application framework (e.g., Django) on the other side. This specification defines an API whose implementation is left open.
|
||||
* **uWSGI** implements the WSGI interface by providing an application server, which connects applications to a web server. A uWSGI application server's main job is to translate HTTP requests into a format that a web application can consume and, afterward, to format the application's response into an HTTP message.
|
||||
* **uwsgi** is a binary protocol implemented by a uWSGI application server to communicate with a full-featured web server such as Nginx; it also includes utilities such as a lightweight web server. The Nginx web server "speaks" uwsgi out of the box.
|
||||
|
||||
|
||||
* WSGI is a Python specification for an interface between a web server on one side, and an application or an application framework (e.g., Django) on the other side. This specification defines an API whose implementation is left open.
|
||||
* uWSGI implements the WSGI interface by providing an application server, which connects applications to a web server. A uWSGI application server's main job is to translate HTTP requests into a format that a web application can consume and, afterward, to format the application's response into an HTTP message.
|
||||
* uwsgi is a binary protocol implemented by a uWSGI application server to communicate with a full-featured web server such as Nginx; it also includes utilities such as a lightweight web server. The Nginx web server "speaks" uwsgi out of the box.
|
||||
|
||||
For convenience, I will use "uwsgi" as shorthand for the binary protocol, the application server, and the very lightweight web server.
|
||||
|
||||
@ -52,33 +46,29 @@ For convenience, I will use "uwsgi" as shorthand for the binary protocol, the ap
|
||||
|
||||
On a Debian-based system, you can install SQLite the usual way (with `%` representing the command-line prompt):
|
||||
|
||||
|
||||
```
|
||||
`% sudo apt-get install sqlite3`
|
||||
% sudo apt-get install sqlite3
|
||||
```
|
||||
|
||||
This database system is a collection of C libraries and utilities, all of which come to about 500KB in size. There is no database server to start, stop, or otherwise maintain.
|
||||
|
||||
Once SQLite is installed, create a database at the command-line prompt:
|
||||
|
||||
|
||||
```
|
||||
`% sqlite3 snowfall.db`
|
||||
% sqlite3 snowfall.db
|
||||
```
|
||||
|
||||
If this succeeds, the command creates the file `snowfall.db` in the current working directory. The database name is arbitrary (e.g., no extension is required), and the command opens the SQLite client utility with `>sqlite` as the prompt:
|
||||
|
||||
|
||||
```
|
||||
Enter ".help" for usage hints.
|
||||
sqlite>
|
||||
sqlite>
|
||||
```
|
||||
|
||||
Create the snowfall table in the snowfall database with the following command. The table name, like the database name, is arbitrary:
|
||||
|
||||
|
||||
```
|
||||
sqlite> CREATE TABLE snowfall (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sqlite> CREATE TABLE snowfall (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
region TEXT NOT NULL,
|
||||
device TEXT NOT NULL,
|
||||
amount DECIMAL NOT NULL,
|
||||
@ -87,9 +77,8 @@ sqlite> CREATE TABLE snowfall (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
SQLite commands are case-insensitive, but it is traditional to use uppercase for SQL terms and lowercase for user terms. Check that the table was created:
|
||||
|
||||
|
||||
```
|
||||
`sqlite> .schema`
|
||||
sqlite> .schema
|
||||
```
|
||||
|
||||
The command echoes the `CREATE TABLE` statement.
|
||||
@ -100,10 +89,9 @@ The database is now ready for business, although the single-table snowfall is em
|
||||
|
||||
Recall that uwsgi can be used in two ways: either as a lightweight web server or as an application server connected to a production-grade web server such as Nginx. The second use is the goal, but the first is suited for developing and testing the programmer's request-handling code. Here's the architecture with Nginx in play as the web server:
|
||||
|
||||
|
||||
```
|
||||
HTTP uwsgi
|
||||
client<\---->Nginx<\----->appServer<\--->request-handling code<\--->SQLite
|
||||
HTTP uwsgi
|
||||
client<---->Nginx<----->appServer<--->request-handling code<--->SQLite
|
||||
```
|
||||
|
||||
The client could be a browser, a utility such as [curl][6], or a hand-crafted program fluent in HTTP. Communications between the client and Nginx occur through HTTP, but then uwsgi takes over as a binary-transport protocol between Nginx and the application server, which interacts with request-handling code such as `requestHandler.py` (described below). This architecture delivers a clean division of labor. Nginx alone manages the client, and only the request-handling code interacts with the database. In turn, the application server separates the web server from the programmer-written code, which has a high-level API to read and write HTTP messages delivered over uwsgi.
|
||||
@ -116,7 +104,6 @@ Below is the source code file `requestHandler.py` for the snowfall application.
|
||||
|
||||
#### The request-handling program
|
||||
|
||||
|
||||
```
|
||||
import sqlite3
|
||||
import cgi
|
||||
@ -127,7 +114,7 @@ PATH_2_DB = '/home/marty/wsgi/snowfall.db'
|
||||
def application(env, start_line):
|
||||
if env['REQUEST_METHOD'] == 'POST': ## add new DB record
|
||||
return handle_post(env, start_line)
|
||||
elif env['REQUEST_METHOD'] == 'GET': ## create HTML-fragment report
|
||||
elif env['REQUEST_METHOD'] == 'GET': ## create HTML-fragment report
|
||||
return handle_get(start_line)
|
||||
else: ## no other option for now
|
||||
start_line('405 METHOD NOT ALLOWED', [('Content-Type', 'text/plain')])
|
||||
@ -136,7 +123,7 @@ def application(env, start_line):
|
||||
|
||||
def handle_post(env, start_line):
|
||||
form = get_field_storage(env) ## body of an HTTP POST request
|
||||
|
||||
|
||||
## Extract fields from POST form.
|
||||
region = form.getvalue('region')
|
||||
device = form.getvalue('device')
|
||||
@ -162,19 +149,19 @@ def handle_get(start_line):
|
||||
cursor = conn.cursor() ## get a cursor
|
||||
cursor.execute("select * from snowfall")
|
||||
|
||||
response_body = "<h3>Snowfall report</h3><ul>"
|
||||
response_body = "<h3>Snowfall report</h3><ul>"
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
response_body += "<li>" + str(row[0]) + '|' ## primary key
|
||||
response_body += "<li>" + str(row[0]) + '|' ## primary key
|
||||
response_body += row[1] + '|' ## region
|
||||
response_body += row[2] + '|' ## device
|
||||
response_body += str(row[3]) + '|' ## amount
|
||||
response_body += str(row[4]) + "</li>" ## timestamp
|
||||
response_body += "</ul>"
|
||||
response_body += str(row[4]) + "</li>" ## timestamp
|
||||
response_body += "</ul>"
|
||||
|
||||
conn.commit() ## commit
|
||||
conn.close() ## cleanup
|
||||
|
||||
|
||||
start_line('200 OK', [('Content-Type', 'text/html')])
|
||||
return [response_body.encode()]
|
||||
|
||||
@ -184,7 +171,7 @@ def add_record(reg, dev, amt, tstamp):
|
||||
cursor = conn.cursor() ## get a cursor
|
||||
|
||||
sql = "INSERT INTO snowfall(region,device,amount,tstamp) values (?,?,?,?)"
|
||||
cursor.execute(sql, (reg, dev, amt, tstamp)) ## execute INSERT
|
||||
cursor.execute(sql, (reg, dev, amt, tstamp)) ## execute INSERT
|
||||
|
||||
conn.commit() ## commit
|
||||
conn.close() ## cleanup
|
||||
@ -203,16 +190,14 @@ def get_field_storage(env):
|
||||
|
||||
A constant at the start of the source file defines the path to the database file:
|
||||
|
||||
|
||||
```
|
||||
`PATH_2_DB = '/home/marty/wsgi/snowfall.db'`
|
||||
PATH_2_DB = '/home/marty/wsgi/snowfall.db'
|
||||
```
|
||||
|
||||
Make sure to update the path for your Raspberry Pi.
|
||||
|
||||
As noted earlier, uwsgi includes a lightweight web server that can host this request-handling application. To begin, install uwsgi with these two commands (`##` introduces my comments):
|
||||
|
||||
|
||||
```
|
||||
% sudo apt-get install build-essential python-dev ## C header files, etc.
|
||||
% pip install uwsgi ## pip = Python package manager
|
||||
@ -220,19 +205,17 @@ As noted earlier, uwsgi includes a lightweight web server that can host this req
|
||||
|
||||
Next, launch a bare-bones snowfall application using uwsgi as the web server:
|
||||
|
||||
|
||||
```
|
||||
`% uwsgi --http 127.0.0.1:9999 --wsgi-file requestHandler.py `
|
||||
% uwsgi --http 127.0.0.1:9999 --wsgi-file requestHandler.py
|
||||
```
|
||||
|
||||
The flag `--http` runs uwsgi in web-server mode, with 9999 as the web server's listening port on localhost (127.0.0.1). By default, uwsgi dispatches HTTP requests to a programmer-defined function named `application`. For review, here's the full function from the top of the `requestHandler.py` code:
|
||||
|
||||
|
||||
```
|
||||
def application(env, start_line):
|
||||
if env['REQUEST_METHOD'] == 'POST': ## add new DB record
|
||||
return handle_post(env, start_line)
|
||||
elif env['REQUEST_METHOD'] == 'GET': ## create HTML-fragment report
|
||||
elif env['REQUEST_METHOD'] == 'GET': ## create HTML-fragment report
|
||||
return handle_get(start_line)
|
||||
else: ## no other option for now
|
||||
start_line('405 METHOD NOT ALLOWED', [('Content-Type', 'text/plain')])
|
||||
@ -242,25 +225,21 @@ def application(env, start_line):
|
||||
|
||||
The snowfall application accepts only two request types:
|
||||
|
||||
* A POST request, if up to snuff, creates a new entry in the snowfall table. The request should include the ski area region, the device in the region, the snowfall amount in centimeters, and a Unix-style timestamp. A POST request is dispatched to the `handle_post` function (which I'll clarify shortly).
|
||||
* A GET request returns an HTML fragment (an unordered list) with the records currently in the snowfall table.
|
||||
|
||||
|
||||
* A POST request, if up to snuff, creates a new entry in the snowfall table. The request should include the ski area region, the device in the region, the snowfall amount in centimeters, and a Unix-style timestamp. A POST request is dispatched to the `handle_post` function (which I'll clarify shortly).
|
||||
* A GET request returns an HTML fragment (an unordered list) with the records currently in the snowfall table.
|
||||
|
||||
Requests with an HTTP verb other than POST and GET will generate an error message.
|
||||
|
||||
You can use a utility such as curl to generate HTTP requests for testing. Here are three sample POST requests to start populating the database:
|
||||
|
||||
|
||||
```
|
||||
% curl -X POST -d "region=R1&device=D9&amount=1.42&tstamp=1604722088.0158753" localhost:9999/
|
||||
% curl -X POST -d "region=R7&device=D4&amount=2.11&tstamp=1604722296.8862638" localhost:9999/
|
||||
% curl -X POST -d "region=R5&device=D1&amount=1.12&tstamp=1604942236.1013834" localhost:9999/
|
||||
% curl -X POST -d "region=R1&device=D9&amount=1.42&tstamp=1604722088.0158753" localhost:9999/
|
||||
% curl -X POST -d "region=R7&device=D4&amount=2.11&tstamp=1604722296.8862638" localhost:9999/
|
||||
% curl -X POST -d "region=R5&device=D1&amount=1.12&tstamp=1604942236.1013834" localhost:9999/
|
||||
```
|
||||
|
||||
These commands add three records to the snowfall table. A subsequent GET request from curl or a browser displays an HTML fragment that lists the rows in the snowfall table. Here's the equivalent as non-HTML text:
|
||||
|
||||
|
||||
```
|
||||
Snowfall report
|
||||
|
||||
@ -273,20 +252,18 @@ A professional report would convert the numeric timestamps into human-readable o
|
||||
|
||||
The uwsgi utility accepts various flags, which can be given either through a configuration file or in the launch command. For example, here's a richer launch of uwsgi as a web server:
|
||||
|
||||
|
||||
```
|
||||
`% uwsgi --master --processes 2 --http 127.0.0.1:9999 --wsgi-file requestHandler.py`
|
||||
% uwsgi --master --processes 2 --http 127.0.0.1:9999 --wsgi-file requestHandler.py
|
||||
```
|
||||
|
||||
This version creates a master (supervisory) process and two worker processes, which can handle the HTTP requests concurrently.
|
||||
|
||||
In the snowfall application, the functions `handle_post` and `handle_get` process POST and GET requests, respectively. Here's the `handle_post` function in full:
|
||||
|
||||
|
||||
```
|
||||
def handle_post(env, start_line):
|
||||
form = get_field_storage(env) ## body of an HTTP POST request
|
||||
|
||||
|
||||
## Extract fields from POST form.
|
||||
region = form.getvalue('region')
|
||||
device = form.getvalue('device')
|
||||
@ -308,18 +285,17 @@ def handle_post(env, start_line):
|
||||
return [response_body.encode()]
|
||||
```
|
||||
|
||||
The two arguments to the `handle_post` function (`env` and `start_line`) represent the system environment and a communications channel, respectively. The `start_line` channel sends the HTTP start line (in this case, either `400 Bad Request` or `201 OK`) and any HTTP headers (in this case, just `Content-Type: text/plain`) of an HTTP response.
|
||||
The two arguments to the `handle_post` function (`env` and `start_line` ) represent the system environment and a communications channel, respectively. The `start_line` channel sends the HTTP start line (in this case, either `400 Bad Request` or `201 OK` ) and any HTTP headers (in this case, just `Content-Type: text/plain` ) of an HTTP response.
|
||||
|
||||
The `handle_post` function tries to extract the relevant data from the HTTP POST request and, if it's successful, calls the function `add_record` to add another row to the snowfall table:
|
||||
|
||||
|
||||
```
|
||||
def add_record(reg, dev, amt, tstamp):
|
||||
conn = sqlite3.connect(PATH_2_DB) ## connect to DB
|
||||
cursor = conn.cursor() ## get a cursor
|
||||
|
||||
sql = "INSERT INTO snowfall(region,device,amount,tstamp) VALUES (?,?,?,?)"
|
||||
cursor.execute(sql, (reg, dev, amt, tstamp)) ## execute INSERT
|
||||
cursor.execute(sql, (reg, dev, amt, tstamp)) ## execute INSERT
|
||||
|
||||
conn.commit() ## commit
|
||||
conn.close() ## cleanup
|
||||
@ -329,26 +305,25 @@ SQLite automatically wraps single SQL statements (such as `INSERT` above) in a t
|
||||
|
||||
The `handle_get` function also touches the database, but only to read the records in the snowfall table:
|
||||
|
||||
|
||||
```
|
||||
def handle_get(start_line):
|
||||
conn = sqlite3.connect(PATH_2_DB) ## connect to DB
|
||||
cursor = conn.cursor() ## get a cursor
|
||||
cursor.execute("SELECT * FROM snowfall")
|
||||
|
||||
response_body = "<h3>Snowfall report</h3><ul>"
|
||||
response_body = "<h3>Snowfall report</h3><ul>"
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
response_body += "<li>" + str(row[0]) + '|' ## primary key
|
||||
response_body += "<li>" + str(row[0]) + '|' ## primary key
|
||||
response_body += row[1] + '|' ## region
|
||||
response_body += row[2] + '|' ## device
|
||||
response_body += str(row[3]) + '|' ## amount
|
||||
response_body += str(row[4]) + "</li>" ## timestamp
|
||||
response_body += "</ul>"
|
||||
response_body += str(row[4]) + "</li>" ## timestamp
|
||||
response_body += "</ul>"
|
||||
|
||||
conn.commit() ## commit
|
||||
conn.close() ## cleanup
|
||||
|
||||
|
||||
start_line('200 OK', [('Content-Type', 'text/html')])
|
||||
return [response_body.encode()]
|
||||
```
|
||||
@ -359,28 +334,25 @@ A user-friendly version of the snowfall application would support additional (an
|
||||
|
||||
The Nginx web server can be installed on a Debian-based system with one command:
|
||||
|
||||
|
||||
```
|
||||
`% sudo apt-get install nginx`
|
||||
% sudo apt-get install nginx
|
||||
```
|
||||
|
||||
As a web server, Nginx provides the expected services, such as wire-level security, HTTPS, user authentication, load balancing, media streaming, response compression, file uploading, etc. The Nginx engine is high-performance and stable, and this server can support dynamic content through a variety of programming languages. Using uwsgi as a very lightweight web server is an attractive option but switching to Nginx is a move up to industrial-strength web hosting with high-volume capability. Nginx and uwsgi are both implemented in C.
|
||||
|
||||
With Nginx in play, uwsgi takes on a communication protocol's restricted roles and an application server; it no longer acts as an HTTP web server. Here's the revised architecture:
|
||||
|
||||
|
||||
```
|
||||
HTTP uwsgi
|
||||
requester<\---->Nginx<\----->app server<\--->requestHandler.py
|
||||
HTTP uwsgi
|
||||
requester<---->Nginx<----->app server<--->requestHandler.py
|
||||
```
|
||||
|
||||
As noted earlier, Nginx includes uwsgi support and now acts as a reverse-proxy server that forwards designated HTTP requests to the uwsgi application server, which in turn interacts with the Python script `requestHandler.py`. Responses from the Python script move in the reverse direction so that Nginx sends the HTTP response back to the requesting client.
|
||||
|
||||
Two changes bring this new architecture to life. The first launches uwsgi as an application server:
|
||||
|
||||
|
||||
```
|
||||
`% uwsgi --socket 127.0.0.1:8001 --wsgi-file requestHandler.py`
|
||||
% uwsgi --socket 127.0.0.1:8001 --wsgi-file requestHandler.py
|
||||
```
|
||||
|
||||
Socket 8001 is the Nginx default for uwsgi communications. For robustness, you could use the full path to the Python script so that the command above does not have to be executed in the directory that houses the Python script. In a production environment, uwsgi would start and stop automatically; for now, however, the emphasis remains on how the architectural pieces fit together.
|
||||
@ -389,7 +361,6 @@ The second change involves Nginx configuration, which can be tricky on Debian-ba
|
||||
|
||||
However the configuration is distributed, the key section for having Nginx talk to the uwsgi application server begins with `http` and has one or more `server` subsections, which in turn have `location` subsections. Here's an example from the Nginx documentation:
|
||||
|
||||
|
||||
```
|
||||
...
|
||||
http {
|
||||
@ -397,7 +368,7 @@ http {
|
||||
...
|
||||
server { # simple reverse-proxy
|
||||
listen 80;
|
||||
server_name domain2.com [www.domain2.com][8];
|
||||
server_name domain2.com www.domain2.com;
|
||||
access_log logs/domain2.access.log main;
|
||||
|
||||
# serve static files
|
||||
@ -408,7 +379,7 @@ http {
|
||||
|
||||
# pass requests for dynamic content to rails/turbogears/zope, et al
|
||||
location / {
|
||||
proxy_pass <http://127.0.0.1:8080>;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
}
|
||||
...
|
||||
@ -417,7 +388,6 @@ http {
|
||||
|
||||
The `location` subsections are the ones of interest. For the snowfall application, here's the added `location` entry with its two configuration lines:
|
||||
|
||||
|
||||
```
|
||||
...
|
||||
server {
|
||||
@ -441,9 +411,8 @@ server {
|
||||
|
||||
To keep things simple for now, make `/snowfall` the only `location` in the configuration. With this configuration in place, Nginx listens on port 80 and dispatches HTTP requests ending with the `/snowfall` path to the uwsgi application server:
|
||||
|
||||
|
||||
```
|
||||
% curl -X POST -d "..." localhost/snowfall ## new POST
|
||||
% curl -X POST -d "..." localhost/snowfall ## new POST
|
||||
% curl -X GET localhost/snowfall ## new GET
|
||||
```
|
||||
|
||||
@ -453,16 +422,14 @@ If the configured location were simply `/` instead of `/snowfall`, then any HTTP
|
||||
|
||||
Once you've changed the Nginx configuration with the added `location` subsection, you can start the web server:
|
||||
|
||||
|
||||
```
|
||||
`% sudo systemctl start nginx`
|
||||
% sudo systemctl start nginx
|
||||
```
|
||||
|
||||
There are other commands similar to `stop` and `restart` Nginx. In a production environment, you could automate these actions so that Nginx starts on a system boot and stops on a system shutdown.
|
||||
|
||||
With uwsgi and Nginx both running, you can use a browser to test whether the architectural components cooperate as expected. For example, if you enter the URL `localhost/` in the browser's input window, then the Nginx welcome page should appear with (HTML) content similar to this:
|
||||
|
||||
|
||||
```
|
||||
Welcome to nginx!
|
||||
...
|
||||
@ -471,7 +438,6 @@ Thank you for using nginx.
|
||||
|
||||
By contrast, the URL `localhost/snowfall` should display the rows currently in the snowfall table:
|
||||
|
||||
|
||||
```
|
||||
Snowfall report
|
||||
|
||||
@ -491,19 +457,18 @@ The software components in the web application work well together and require ve
|
||||
via: https://opensource.com/article/21/3/web-hosting-raspberry-pi
|
||||
|
||||
作者:[Marty Kalin][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mkalindepauledu
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_web_internet_website.png?itok=g5B_Bw62 (Digital creative of a browser on the internet)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/browser_web_internet_website.png
|
||||
[2]: https://www.raspberrypi.org/products/raspberry-pi-4-model-b/
|
||||
[3]: https://opensource.com/article/21/2/sqlite3-cheat-sheet
|
||||
[4]: https://en.wikipedia.org/wiki/ACID
|
||||
[5]: https://uwsgi-docs.readthedocs.io/en/latest/
|
||||
[6]: https://opensource.com/article/20/5/curl-cheat-sheet
|
||||
[7]: https://condor.depaul.edu/mkalin
|
||||
[8]: http://www.domain2.com
|
||||
|
@ -1,18 +1,20 @@
|
||||
[#]: subject: (Manage containers on Raspberry Pi with this open source tool)
|
||||
[#]: via: (https://opensource.com/article/21/3/bastille-raspberry-pi)
|
||||
[#]: author: (Peter Czanik https://opensource.com/users/czanik)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Use FreeBSD jails on Raspberry Pi"
|
||||
[#]: via: "https://opensource.com/article/21/3/bastille-raspberry-pi"
|
||||
[#]: author: "Peter Czanik https://opensource.com/users/czanik"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Manage containers on Raspberry Pi with this open source tool
|
||||
Use FreeBSD jails on Raspberry Pi
|
||||
======
|
||||
Create and maintain your containers (aka jails) at scale on FreeBSD with
|
||||
Bastille.
|
||||
Create and maintain your containers (aka jails) at scale on FreeBSD with Bastille.
|
||||
|
||||
![Parts, modules, containers for software][1]
|
||||
|
||||
Image by: Opensource.com
|
||||
|
||||
Containers became widely popular because of Docker on Linux, but there are [much earlier implementations][2], including the [jail][3] system on FreeBSD. A container is called a "jail" in FreeBSD terminology. The jail system was first released in FreeBSD 4.0 way back in 2000, and it has continuously improved since. While 20 years ago it was used mostly on large servers, now you can run it on your Raspberry Pi.
|
||||
|
||||
### Jails vs. containers on Linux
|
||||
@ -31,19 +33,17 @@ Docker brought popularity, accessibility, and ease of use to containers. There a
|
||||
|
||||
Installing [BSD on Raspberry Pi][8] is pretty similar to installing Linux. You download a compressed image from the FreeBSD website and `dd` it to an SD card. You can also use a dedicated image writer tool; there are many available for all operating systems (OS). Download and write an image from the command line with:
|
||||
|
||||
|
||||
```
|
||||
wget <https://download.freebsd.org/ftp/releases/arm64/aarch64/ISO-IMAGES/13.0/FreeBSD-13.0-BETA1-arm64-aarch64-RPI.img.xz>
|
||||
wget https://download.freebsd.org/ftp/releases/arm64/aarch64/ISO-IMAGES/13.0/FreeBSD-13.0-BETA1-arm64-aarch64-RPI.img.xz
|
||||
xzcat FreeBSD-13.0-BETA1-arm64-aarch64-RPI.img.xz | dd of=/dev/XXX
|
||||
```
|
||||
|
||||
That writes the latest beta image available for 64-bit Raspberry Pi boards; check the [download page][9] if you use another Raspberry Pi board or want to use another build. Replace `XXX` with your SD card's device name, which depends on your OS and how the card connects to your machine. I purposefully did not use a device name so that you won't overwrite anything if you just copy and paste the instructions mindlessly. I did that and was lucky to have a recent backup of my laptop, but it was _not_ a pleasant experience.
|
||||
That writes the latest beta image available for 64-bit Raspberry Pi boards; check the [download page][9] if you use another Raspberry Pi board or want to use another build. Replace `XXX` with your SD card's device name, which depends on your OS and how the card connects to your machine. I purposefully did not use a device name so that you won't overwrite anything if you just copy and paste the instructions mindlessly. I did that and was lucky to have a recent backup of my laptop, but it was *not* a pleasant experience.
|
||||
|
||||
Once you've written the SD card, put it in your Raspberry Pi and boot it. The first boot takes a bit longer than usual; I suspect the partition sizes are being adjusted to the SD card's size. After a while, you will receive the familiar login prompt on a good old text-based screen. The username is **root**, and the password is the same as the user name. The SSH server is enabled by default, but don't worry; the root user cannot log in. It is still a good idea to change the password to something else. The network is automatically configured by DHCP for the Ethernet connection (I did not test WiFi).
|
||||
|
||||
The easiest way to configure Bastille on the system is to SSH into Raspberry Pi and copy and paste the commands and configuration in this article. You have a couple of options, depending on how much you care about industry best practices or are willing to treat it as a test system. You can either enable root login in the SSHD configuration (scary, but this is what I did at first) or create a regular user that can log in remotely. In the latter case, make sure that the user is part of the "wheel" group so that it can use `su -` to become root and use Bastille:
|
||||
|
||||
|
||||
```
|
||||
root@generic:~ # adduser
|
||||
Username: czanik
|
||||
@ -79,9 +79,8 @@ Goodbye!
|
||||
|
||||
The fifth line adds the user to the wheel group. Note that you might have a different list of shells on your system, and Bash is not part of the base system. Install Bash before adding the user:
|
||||
|
||||
|
||||
```
|
||||
`pkg install bash`
|
||||
pkg install bash
|
||||
```
|
||||
|
||||
PKG needs to bootstrap itself on the first run, so invoking the command takes a bit longer this time.
|
||||
@ -90,34 +89,30 @@ PKG needs to bootstrap itself on the first run, so invoking the command takes a
|
||||
|
||||
Managing jails with the tools in the FreeBSD base system is possible—but not really convenient. Using a tool like Bastille can simplify it considerably. It is not part of the base system, so install it:
|
||||
|
||||
|
||||
```
|
||||
`pkg install bastille`
|
||||
pkg install bastille
|
||||
```
|
||||
|
||||
As you can see from the command's output, Bastille has no external dependencies. It is a shell script that relies on commands in the FreeBSD base system (with an exception I'll note later when explaining templates).
|
||||
|
||||
If you want to start your containers on boot, enable Bastille:
|
||||
|
||||
|
||||
```
|
||||
`sysrc bastille_enable="YES"`
|
||||
sysrc bastille_enable="YES"
|
||||
```
|
||||
|
||||
Start with a simple use case. Many people use containers to install different development tools in different containers to avoid conflicts or simplify their environments. For example, no sane person wants to install Python 2 on a brand-new system—but you might need to run an ancient script every once in a while. So, create a jail for Python 2.
|
||||
|
||||
Before creating your first jail, you need to bootstrap a FreeBSD release and configure networking. Just make sure that you bootstrap the same or an older release than the host is running. For example:
|
||||
|
||||
|
||||
```
|
||||
`bastille bootstrap 12.2-RELEASE`
|
||||
bastille bootstrap 12.2-RELEASE
|
||||
```
|
||||
|
||||
It downloads and extracts this release under the `/usr/local/bastille` directory structure.
|
||||
|
||||
Networking can be configured in many different ways using Bastille. One option that works everywhere—on your local machine and in the cloud—is using cloned interfaces. This allows jails to use an internal network that does not interfere with the external network. Configure and start this internal network:
|
||||
|
||||
|
||||
```
|
||||
sysrc cloned_interfaces+=lo1
|
||||
sysrc ifconfig_lo1_name="bastille0"
|
||||
@ -126,7 +121,6 @@ service netif cloneup
|
||||
|
||||
With this network setup, services in your jails are not accessible from the outside network, nor can they reach outside. You need forward ports from your host's external interface to the jails and to enable network access translation (NAT). Bastille integrates with BSD's [PF firewall][10] for this task. The following `pf.conf` configures the PF firewall such that Bastille can add port forwarding rules to the firewall dynamically:
|
||||
|
||||
|
||||
```
|
||||
ext_if="ue0"
|
||||
|
||||
@ -134,8 +128,8 @@ set block-policy return
|
||||
scrub in on $ext_if all fragment reassemble
|
||||
set skip on lo
|
||||
|
||||
table <jails> persist
|
||||
nat on $ext_if from <jails> to any -> ($ext_if)
|
||||
table <jails> persist
|
||||
nat on $ext_if from <jails> to any -> ($ext_if)
|
||||
|
||||
rdr-anchor "rdr/*"
|
||||
|
||||
@ -147,7 +141,6 @@ pass in inet proto tcp from any to any port ssh flags S/SA modulate state
|
||||
|
||||
You also need to enable and start PF for these rules to take effect. Note that if you work through an SSH connection, starting PF will terminate your connection, and you will need to log in again:
|
||||
|
||||
|
||||
```
|
||||
sysrc pf_enable="YES"
|
||||
service pf restart
|
||||
@ -157,14 +150,12 @@ service pf restart
|
||||
|
||||
To create a jail, Bastille needs a few parameters. First, it needs a name for the jail you're creating. It is an important parameter, as you will always refer to a jail by its name. I chose the name of the most famous Hungarian jail for the most elite criminals, but in real life, jail names often refer to the jail's function, like `syslogserver`. You also need to set the FreeBSD release you're using and an internet protocol (IP) address. I used a random `10.0.0.0/8` IP address range, but if your internal network already uses addresses from that, then using the `192.168.0.0/16` is probably a better idea:
|
||||
|
||||
|
||||
```
|
||||
`bastille create csillag 12.2-RELEASE 10.17.89.51`
|
||||
bastille create csillag 12.2-RELEASE 10.17.89.51
|
||||
```
|
||||
|
||||
Your new jail should be up and running within a few seconds. It is a complete FreeBSD base system without any extra packages. So install some packages, like my favorite text editor, inside the jail:
|
||||
|
||||
|
||||
```
|
||||
root@generic:~ # bastille pkg csillag install joe
|
||||
[csillag]:
|
||||
@ -190,22 +181,20 @@ Checking integrity... done (0 conflicting)
|
||||
|
||||
You can install multiple packages at the same time. Install Python 2, Bash, and Git:
|
||||
|
||||
|
||||
```
|
||||
`bastille pkg csillag install bash python2 git`
|
||||
bastille pkg csillag install bash python2 git
|
||||
```
|
||||
|
||||
Now you can start working in your new, freshly created jail. There are no network services installed in it, but you can reach it through its console:
|
||||
|
||||
|
||||
```
|
||||
root@generic:~ # bastille console csillag
|
||||
[csillag]:
|
||||
root@csillag:~ # python2
|
||||
Python 2.7.18 (default, Feb 2 2021, 01:53:44)
|
||||
[GCC FreeBSD Clang 10.0.1 ([git@github.com][11]:llvm/llvm-project.git llvmorg-10.0.1- on freebsd12
|
||||
[GCC FreeBSD Clang 10.0.1 (git@github.com:llvm/llvm-project.git llvmorg-10.0.1- on freebsd12
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>>
|
||||
>>>
|
||||
root@csillag:~ # logout
|
||||
|
||||
root@generic:~ #
|
||||
@ -217,21 +206,18 @@ The previous example manually installed some packages inside a jail. Setting up
|
||||
|
||||
To use templates, you need to install Git on the host:
|
||||
|
||||
|
||||
```
|
||||
`pkg install git`
|
||||
pkg install git
|
||||
```
|
||||
|
||||
For example, to bootstrap the `syslog-ng` template, use:
|
||||
|
||||
|
||||
```
|
||||
`bastille bootstrap https://gitlab.com/BastilleBSD-Templates/syslog-ng`
|
||||
bastille bootstrap https://gitlab.com/BastilleBSD-Templates/syslog-ng
|
||||
```
|
||||
|
||||
Create a new jail, apply the template, and redirect an external port to it:
|
||||
|
||||
|
||||
```
|
||||
bastille create alcatraz 12.2-RELEASE 10.17.89.50
|
||||
bastille template alcatraz BastilleBSD-Templates/syslog-ng
|
||||
@ -240,7 +226,6 @@ bastille rdr alcatraz tcp 514 514
|
||||
|
||||
To test the new service within the jail, use telnet to connect port 514 of your host and enter some random text. Use the `tail` command within your jail to see what you just entered:
|
||||
|
||||
|
||||
```
|
||||
root@generic:~ # tail /usr/local/bastille/jails/alcatraz/root/var/log/messages
|
||||
Feb 6 03:57:27 alcatraz sendmail[3594]: gethostbyaddr(10.17.89.50) failed: 1
|
||||
@ -249,26 +234,26 @@ Feb 6 04:07:18 192.168.1.126 this is a test
|
||||
Feb 6 04:07:20 alcatraz syslog-ng[1186]: Syslog connection closed; fd='23', client='AF_INET(192.168.1.126:50104)', local='AF_INET(0.0.0.0:514)'
|
||||
```
|
||||
|
||||
Since I'm a [syslog-ng][12] evangelist, I used the syslog-ng template in my example, but there are many more available. Check the full list of [Bastille templates][13] to learn about them.
|
||||
Since I'm a [syslog-ng][11] evangelist, I used the syslog-ng template in my example, but there are many more available. Check the full list of [Bastille templates][12] to learn about them.
|
||||
|
||||
### What's next?
|
||||
|
||||
I hope that this article inspires you to try FreeBSD and Bastille on your Raspberry Pi. It was just enough information to get you started; to learn about all of Bastille's cool features—like auditing your jails for vulnerabilities and updating software within them—in the [documentation][14].
|
||||
I hope that this article inspires you to try FreeBSD and Bastille on your Raspberry Pi. It was just enough information to get you started; to learn about all of Bastille's cool features—like auditing your jails for vulnerabilities and updating software within them—in the [documentation][13].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/3/bastille-raspberry-pi
|
||||
|
||||
作者:[Peter Czanik][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/czanik
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/containers_modules_networking_hardware_parts.png?itok=rPpVj92- (Parts, modules, containers for software)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/containers_modules_networking_hardware_parts.png
|
||||
[2]: https://opensource.com/article/18/1/history-low-level-container-runtimes
|
||||
[3]: https://docs.freebsd.org/en/books/handbook/jails/
|
||||
[4]: https://opensource.com/article/18/11/behind-scenes-linux-containers
|
||||
@ -278,7 +263,6 @@ via: https://opensource.com/article/21/3/bastille-raspberry-pi
|
||||
[8]: https://opensource.com/article/19/3/netbsd-raspberry-pi
|
||||
[9]: https://www.freebsd.org/where/
|
||||
[10]: https://en.wikipedia.org/wiki/PF_(firewall)
|
||||
[11]: mailto:git@github.com
|
||||
[12]: https://www.syslog-ng.com/
|
||||
[13]: https://gitlab.com/BastilleBSD-Templates/
|
||||
[14]: https://bastille.readthedocs.io/en/latest/
|
||||
[11]: https://www.syslog-ng.com/
|
||||
[12]: https://gitlab.com/BastilleBSD-Templates/
|
||||
[13]: https://bastille.readthedocs.io/en/latest/
|
@ -1,18 +1,20 @@
|
||||
[#]: subject: (Get started with edge computing by programming embedded systems)
|
||||
[#]: via: (https://opensource.com/article/21/3/rtos-embedded-development)
|
||||
[#]: author: (Alan Smithee https://opensource.com/users/alansmithee)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Get started with edge computing by programming embedded systems"
|
||||
[#]: via: "https://opensource.com/article/21/3/rtos-embedded-development"
|
||||
[#]: author: "Alan Smithee https://opensource.com/users/alansmithee"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Get started with edge computing by programming embedded systems
|
||||
======
|
||||
The AT device package for controlling wireless modems is one of RTOS's
|
||||
most popular extensions.
|
||||
The AT device package for controlling wireless modems is one of RTOS's most popular extensions.
|
||||
|
||||
![Looking at a map][1]
|
||||
|
||||
Image by: opensource.com
|
||||
|
||||
RTOS is an open source [operating system for embedded devices][2] developed by RT-Thread. It provides a standardized, friendly foundation for developers to program a variety of devices and includes a large number of useful libraries and toolkits to make the process easier.
|
||||
|
||||
Like Linux, RTOS uses a modular approach, which makes it easy to extend. Packages enable developers to use RTOS for any device they want to target. One of RTOS's most popular extensions is the AT device package, which includes porting files and sample code for different AT devices (i.e., modems).
|
||||
@ -37,77 +39,71 @@ The at_device package is distributed under an LGPLv2.1 license, and it's easy to
|
||||
|
||||
To use AT devices with RTOS, you must enable the AT component library and AT socket functionality. This requires:
|
||||
|
||||
* RT_Thread 4.0.2+
|
||||
* RT_Thread AT component 1.3.0+
|
||||
* RT_Thread SAL component
|
||||
* RT-Thread netdev component
|
||||
|
||||
|
||||
* RT_Thread 4.0.2+
|
||||
* RT_Thread AT component 1.3.0+
|
||||
* RT_Thread SAL component
|
||||
* RT-Thread netdev component
|
||||
|
||||
The AT device package has been updated for multiple versions. Different versions require different configuration options, so they must fit into the corresponding system versions. Most of the currently available AT device package versions are:
|
||||
|
||||
* V1.2.0: For RT-Thread versions less than V3.1.3, AT component version equals V1.0.0
|
||||
* V1.3.0: For RT-Thread versions less than V3.1.3, AT component version equals V1.1.0
|
||||
* V1.4.0: For RT-Thread versions less than V3.1.3 or equal to V4.0.0, AT component version equals V1.2.0
|
||||
* V1.5.0: For RT-Thread versions less than V3.1.3 or equal to V4.0.0, AT component version equals V1.2.0
|
||||
* V1.6.0: For RT-Thread versions equal to V3.1.3 or V4.0.1, AT component version equals V1.2.0
|
||||
* V2.0.0/V2.0.1: For RT-Thread versions higher than V4.0.1 or higher than 3.1.3, AT component version equals V1.3.0
|
||||
* Latest version: For RT-Thread versions higher than V4.0.1 or higher than 3.1.3, AT component version equals V1.3.0
|
||||
|
||||
|
||||
* V1.2.0: For RT-Thread versions less than V3.1.3, AT component version equals V1.0.0
|
||||
* V1.3.0: For RT-Thread versions less than V3.1.3, AT component version equals V1.1.0
|
||||
* V1.4.0: For RT-Thread versions less than V3.1.3 or equal to V4.0.0, AT component version equals V1.2.0
|
||||
* V1.5.0: For RT-Thread versions less than V3.1.3 or equal to V4.0.0, AT component version equals V1.2.0
|
||||
* V1.6.0: For RT-Thread versions equal to V3.1.3 or V4.0.1, AT component version equals V1.2.0
|
||||
* V2.0.0/V2.0.1: For RT-Thread versions higher than V4.0.1 or higher than 3.1.3, AT component version equals V1.3.0
|
||||
* Latest version: For RT-Thread versions higher than V4.0.1 or higher than 3.1.3, AT component version equals V1.3.0
|
||||
|
||||
Getting the right version is mostly an automatic process done in menuconfig. It provides the best version of the at_device package based on your current system environment.
|
||||
|
||||
As mentioned, different versions require different configuration options. For instance, version 1.x supports enabling one AT device at a time:
|
||||
|
||||
|
||||
```
|
||||
RT-Thread online packages --->
|
||||
IoT - internet of things --->
|
||||
RT-Thread online packages --->
|
||||
IoT - internet of things --->
|
||||
-*- AT DEVICE: RT-Thread AT component porting or samples for different device
|
||||
[ ] Enable at device init by thread
|
||||
AT socket device modules (Not selected, please select) --->
|
||||
Version (V1.6.0) --->
|
||||
AT socket device modules (Not selected, please select) --->
|
||||
Version (V1.6.0) --->
|
||||
```
|
||||
|
||||
The option to enable the AT device init by thread dictates whether the configuration creates a separate thread to initialize the device network.
|
||||
|
||||
Version 2.x supports enabling multiple AT devices at the same time:
|
||||
|
||||
|
||||
```
|
||||
RT-Thread online packages --->
|
||||
IoT - internet of things --->
|
||||
RT-Thread online packages --->
|
||||
IoT - internet of things --->
|
||||
-*- AT DEVICE: RT-Thread AT component porting or samples for different device
|
||||
[*] Quectel M26/MC20 --->
|
||||
[*] Quectel M26/MC20 --->
|
||||
[*] Enable initialize by thread
|
||||
[*] Enable sample
|
||||
(-1) Power pin
|
||||
(-1) Power status pin
|
||||
(uart3) AT client device name
|
||||
(512) The maximum length of receive line buffer
|
||||
[ ] Quectel EC20 --->
|
||||
[ ] Espressif ESP32 --->
|
||||
[*] Espressif ESP8266 --->
|
||||
[ ] Quectel EC20 --->
|
||||
[ ] Espressif ESP32 --->
|
||||
[*] Espressif ESP8266 --->
|
||||
[*] Enable initialize by thread
|
||||
[*] Enable sample
|
||||
(realthread) WIFI ssid
|
||||
(12345678) WIFI password
|
||||
(uart2) AT client device name
|
||||
(512) The maximum length of receive line buffer
|
||||
[ ] Realthread RW007 --->
|
||||
[ ] SIMCom SIM800C --->
|
||||
[ ] SIMCom SIM76XX --->
|
||||
[ ] Notion MW31 --->
|
||||
[ ] WinnerMicro W60X --->
|
||||
[ ] AiThink A9/A9G --->
|
||||
[ ] Quectel BC26 --->
|
||||
[ ] Luat air720 --->
|
||||
[ ] GOSUNCN ME3616 --->
|
||||
[ ] ChinaMobile M6315 --->
|
||||
[ ] Quectel BC28 --->
|
||||
[ ] Quectel ec200x --->
|
||||
Version (latest) --->
|
||||
[ ] Realthread RW007 --->
|
||||
[ ] SIMCom SIM800C --->
|
||||
[ ] SIMCom SIM76XX --->
|
||||
[ ] Notion MW31 --->
|
||||
[ ] WinnerMicro W60X --->
|
||||
[ ] AiThink A9/A9G --->
|
||||
[ ] Quectel BC26 --->
|
||||
[ ] Luat air720 --->
|
||||
[ ] GOSUNCN ME3616 --->
|
||||
[ ] ChinaMobile M6315 --->
|
||||
[ ] Quectel BC28 --->
|
||||
[ ] Quectel ec200x --->
|
||||
Version (latest) --->
|
||||
```
|
||||
|
||||
This version includes many other options, including one to enable sample code, which might be particularly useful to new developers or any developer using an unfamiliar device.
|
||||
@ -116,24 +112,21 @@ You can also control options to choose which pin you want to use to supply power
|
||||
|
||||
In short, there is no shortage of control options.
|
||||
|
||||
* V2.X.X version supports enabling multiple AT devices simultaneously, and the enabled device information can be viewed with the `ifocnfig` command in [finsh shell][6].
|
||||
* V2.X.X version requires the device to register before it's used; the registration can be done in the samples directory file or customized in the application layer.
|
||||
* Pin options such as **Power pin** and **Power status pin** are configured according to the device's hardware connection. They can be configured as `-1` if the hardware power-on function is not used.
|
||||
* One AT device should correspond to one serial name, and the **AT client device name** for each device should be different.
|
||||
|
||||
|
||||
* V2.X.X version supports enabling multiple AT devices simultaneously, and the enabled device information can be viewed with the `ifocnfig` command in [finsh shell][6].
|
||||
* V2.X.X version requires the device to register before it's used; the registration can be done in the samples directory file or customized in the application layer.
|
||||
* Pin options such as Power pin and Power status pin are configured according to the device's hardware connection. They can be configured as `-1` if the hardware power-on function is not used.
|
||||
* One AT device should correspond to one serial name, and the AT client device name for each device should be different.
|
||||
|
||||
### AT components configuration options
|
||||
|
||||
When the AT device package is selected and device support is enabled, client functionality for the AT component is selected by default. That means more options—this time for the AT component:
|
||||
|
||||
|
||||
```
|
||||
RT-Thread Components --->
|
||||
Network --->
|
||||
AT commands --->
|
||||
RT-Thread Components --->
|
||||
Network --->
|
||||
AT commands --->
|
||||
[ ] Enable debug log output
|
||||
[ ] Enable AT commands server
|
||||
[ ] Enable AT commands server
|
||||
-*- Enable AT commands client
|
||||
(1) The maximum number of supported clients
|
||||
-*- Enable BSD Socket API support by AT commnads
|
||||
@ -144,11 +137,9 @@ RT-Thread Components --->
|
||||
|
||||
The configuration options related to the AT device package are:
|
||||
|
||||
* **The maximum number of supported clients**: Selecting multiple devices in the AT device package requires this option to be configured as the corresponding value.
|
||||
* **Enable BSD Socket API support by AT commands**: This option will be selected by default when selecting the AT device package.
|
||||
* **The maximum length of AT Commands buffe:** The maximum length of the data the AT commands can send.
|
||||
|
||||
|
||||
* The maximum number of supported clients: Selecting multiple devices in the AT device package requires this option to be configured as the corresponding value.
|
||||
* Enable BSD Socket API support by AT commands: This option will be selected by default when selecting the AT device package.
|
||||
* The maximum length of AT Commands buffe: The maximum length of the data the AT commands can send.
|
||||
|
||||
### Anything is possible
|
||||
|
||||
@ -159,15 +150,15 @@ When you start programming embedded systems, you quickly realize that you can cr
|
||||
via: https://opensource.com/article/21/3/rtos-embedded-development
|
||||
|
||||
作者:[Alan Smithee][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alansmithee
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/tips_map_guide_ebook_help_troubleshooting_lightbulb_520.png?itok=L0BQHgjr (Looking at a map)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/tips_map_guide_ebook_help_troubleshooting_lightbulb_520.png
|
||||
[2]: https://opensource.com/article/20/6/open-source-rtos
|
||||
[3]: https://en.wikipedia.org/wiki/Berkeley_sockets
|
||||
[4]: https://github.com/RT-Thread/rtthread-manual-doc/blob/master/at/at.md
|
||||
|
@ -1,19 +1,20 @@
|
||||
[#]: subject: (Get started with an open source customer data platform)
|
||||
[#]: via: (https://opensource.com/article/21/3/rudderstack-customer-data-platform)
|
||||
[#]: author: (Amey Varangaonkar https://opensource.com/users/ameypv)
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: "Get started with an open source customer data platform"
|
||||
[#]: via: "https://opensource.com/article/21/3/rudderstack-customer-data-platform"
|
||||
[#]: author: "Amey Varangaonkar https://opensource.com/users/ameypv"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: " "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Get started with an open source customer data platform
|
||||
======
|
||||
As an open source alternative to Segment, RudderStack collects and
|
||||
routes event stream (or clickstream) data and automatically builds your
|
||||
customer data lake on your data warehouse.
|
||||
As an open source alternative to Segment, RudderStack collects and routes event stream (or clickstream) data and automatically builds your customer data lake on your data warehouse.
|
||||
|
||||
![Person standing in front of a giant computer screen with numbers, data][1]
|
||||
|
||||
Image by: Opensource.com
|
||||
|
||||
[RudderStack][2] is an open source, warehouse-first customer data pipeline. It collects and routes event stream (or clickstream) data and automatically builds your customer data lake on your data warehouse.
|
||||
|
||||
RudderStack is commonly known as the open source alternative to the customer data platform (CDP), [Segment][3]. It provides a more secure, flexible, and cost-effective solution in comparison. You get all the CDP functionality with added security and full ownership of your customer data.
|
||||
@ -24,60 +25,51 @@ Warehouse-first tools like RudderStack are architected to build functional data
|
||||
|
||||
Before you get started, you will need the RudderStack workspace token from your RudderStack dashboard. To get it:
|
||||
|
||||
1. Go to the [RudderStack dashboard][4].
|
||||
|
||||
2. Log in using your credentials (or sign up for an account, if you don't already have one).
|
||||
|
||||
![RudderStack login screen][5]
|
||||
|
||||
(RudderStack, [CC BY-SA 4.0][6])
|
||||
|
||||
3. Once you've logged in, you should see the workspace token on your RudderStack dashboard.
|
||||
|
||||
![RudderStack workspace token][7]
|
||||
|
||||
(RudderStack, [CC BY-SA 4.0][6])
|
||||
1. Go to the [RudderStack dashboard][4].
|
||||
2. Log in using your credentials (or sign up for an account, if you don't already have one).
|
||||
|
||||
![RudderStack login screen][7]
|
||||
|
||||
3. Once you've logged in, you should see the workspace token on your RudderStack dashboard.
|
||||
|
||||
![RudderStack workspace token][8]
|
||||
|
||||
### Installing RudderStack
|
||||
|
||||
Setting up a RudderStack open source instance is straightforward. You have two installation options:
|
||||
|
||||
1. On your Kubernetes cluster, using RudderStack's Helm charts
|
||||
2. On your Docker container, using the `docker-compose` command
|
||||
1. On your Kubernetes cluster, using RudderStack's Helm charts
|
||||
2. On your Docker container, using the `docker-compose` command
|
||||
|
||||
|
||||
|
||||
This tutorial explains how to use both options but assumes that you already have [Git installed on your system][8].
|
||||
This tutorial explains how to use both options but assumes that you already have [Git installed on your system][9].
|
||||
|
||||
#### Deploying with Kubernetes
|
||||
|
||||
You can deploy RudderStack on your Kubernetes cluster using the [Helm][9] package manager.
|
||||
You can deploy RudderStack on your Kubernetes cluster using the [Helm][10] package manager.
|
||||
|
||||
_If you plan to use RudderStack in production, we strongly recommend using this method._ This is because the Docker images are updated with bug fixes more frequently than the GitHub repository (which follows a monthly release cycle).
|
||||
*If you plan to use RudderStack in production, we strongly recommend using this method.* This is because the Docker images are updated with bug fixes more frequently than the GitHub repository (which follows a monthly release cycle).
|
||||
|
||||
Before you can deploy RudderStack on Kubernetes, make sure you have the following prerequisites in place:
|
||||
|
||||
* [Install and connect kubectl][10] to your Kubernetes cluster.
|
||||
* [Install Helm][11] on your system, either through the Helm installer scripts or its package manager.
|
||||
* Finally, get the workspace token from the RudderStack dashboard by following the steps in the [Getting the RudderStack workspace token][12] section.
|
||||
|
||||
|
||||
* [Install and connect kubectl][11] to your Kubernetes cluster.
|
||||
* [Install Helm][12] on your system, either through the Helm installer scripts or its package manager.
|
||||
* Finally, get the workspace token from the RudderStack dashboard by following the steps in the Getting the RudderStack workspace token section.
|
||||
|
||||
Once you've completed all the prerequisites, deploy RudderStack on your default Kubernetes cluster:
|
||||
|
||||
1. Find the Helm chart required to deploy RudderStack in this [repo][13].
|
||||
2. Install the Helm chart with a release name of your choice (`my-release`, in this example) from the root directory of the repo in the previous step: [code] $ helm install \
|
||||
my-release ./ --set \
|
||||
rudderWorkspaceToken="<your workspace token from RudderStack dashboard>"
|
||||
```
|
||||
1. Find the Helm chart required to deploy RudderStack in this [repo][13].
|
||||
2. Install the Helm chart with a release name of your choice (my-release, in this example) from the root directory of the repo in the previous step:
|
||||
```
|
||||
$ helm install \
|
||||
my-release ./ --set \
|
||||
rudderWorkspaceToken="<your workspace token from RudderStack dashboard>"
|
||||
```
|
||||
|
||||
This deploys RudderStack on your default Kubernetes cluster configured with kubectl using the workspace token you obtained from the RudderStack dashboard.
|
||||
|
||||
For more details on the configurable parameters in the RudderStack Helm chart or updating the versions of the images used, consult the [documentation][14].
|
||||
|
||||
### Deploying with Docker
|
||||
#### Deploying with Docker
|
||||
|
||||
Docker is the easiest and fastest way to set up your open source RudderStack instance.
|
||||
|
||||
@ -85,12 +77,12 @@ First, get the workspace token from the RudderStack dashboard by following the s
|
||||
|
||||
Once you have the RudderStack workspace token:
|
||||
|
||||
1. Download the [**rudder-docker.yml**][15] docker-compose file required for the installation.
|
||||
2. Replace `<your_workspace_token>` in this file with your RudderStack workspace token.
|
||||
3. Set up RudderStack on your Docker container by running: [code]`docker-compose -f rudder-docker.yml up`
|
||||
```
|
||||
|
||||
|
||||
1. Download the [rudder-docker.yml][15] docker-compose file required for the installation.
|
||||
2. Replace `<your_workspace_token>` in this file with your RudderStack workspace token.
|
||||
3. Set up RudderStack on your Docker container by running:
|
||||
```
|
||||
docker-compose -f rudder-docker.yml up
|
||||
```
|
||||
|
||||
Now RudderStack should be up and running on your Docker instance.
|
||||
|
||||
@ -98,128 +90,115 @@ Now RudderStack should be up and running on your Docker instance.
|
||||
|
||||
You can verify your RudderStack installation by sending test events using the bundled shell script:
|
||||
|
||||
1. Clone the GitHub repository: [code]`git clone https://github.com/rudderlabs/rudder-server.git`
|
||||
```
|
||||
2. In this tutorial, you will verify RudderStack by sending test events to Google Analytics. Make sure you have a Google Analytics account and keep the tracking ID handy. Also, note that the Google Analytics account needs to have a `Web` property.
|
||||
1. Clone the GitHub repository:
|
||||
```
|
||||
git clone https://github.com/rudderlabs/rudder-server.git
|
||||
```
|
||||
2. In this tutorial, you will verify RudderStack by sending test events to Google Analytics. Make sure you have a Google Analytics account and keep the tracking ID handy. Also, note that the Google Analytics account needs to have a `Web` property.
|
||||
3. In the [RudderStack hosted control plane][16]:
|
||||
* Add a source on the RudderStack dashboard by following the [Adding a source and destination in RudderStack][17] guide. You can use either of RudderStack's event stream software development kits (SDKs) for sending events from your app. This example sets up the [JavaScript SDK][18] as a source on the dashboard. Note: You aren't actually installing the RudderStack JavaScript SDK on your site in this step; you are just creating the source in RudderStack.
|
||||
* Configure a Google Analytics destination on the RudderStack dashboard using the instructions in the guide mentioned previously. Use the Google Analytics tracking ID you kept from step 2 of this section:
|
||||
|
||||
3. In the [RudderStack hosted control plane][4]:
|
||||
|
||||
* Add a source on the RudderStack dashboard by following the [Adding a source and destination in RudderStack][16] guide. You can use either of RudderStack's event stream software development kits (SDKs) for sending events from your app. This example sets up the [JavaScript SDK][17] as a source on the dashboard. **Note:** You aren't actually installing the RudderStack JavaScript SDK on your site in this step; you are just creating the source in RudderStack.
|
||||
|
||||
* Configure a Google Analytics destination on the RudderStack dashboard using the instructions in the guide mentioned previously. Use the Google Analytics tracking ID you kept from step 2 of this section:
|
||||
|
||||
![Google Analytics tracking ID][18]
|
||||
|
||||
(RudderStack, [CC BY-SA 4.0][6])
|
||||
|
||||
4. As mentioned before, RudderStack bundles a shell script that generates test events. Get the **Source write key** from the RudderStack dashboard:
|
||||
|
||||
![RudderStack source write key][19]
|
||||
|
||||
(RudderStack, [CC BY-SA 4.0][6])
|
||||
|
||||
5. Next, run: [code]`./scripts/generate-event <YOUR_WRITE_KEY> https://hosted.rudderlabs.com/v1/batch`
|
||||
```
|
||||
|
||||
6. Finally, log into your Google Analytics account and verify that the events were delivered. In your Google Analytics account, navigate to **RealTime** -> **Events**. The RealTime view is important because some dashboards can take one to two days to refresh.
|
||||
![Google Analytics tracking ID][27]
|
||||
|
||||
4. As mentioned before, RudderStack bundles a shell script that generates test events. Get the **Source write key** from the RudderStack dashboard:
|
||||
|
||||
![RudderStack source write key][28]
|
||||
|
||||
5. Next, run:
|
||||
```
|
||||
./scripts/generate-event <YOUR_WRITE_KEY> https://hosted.rudderlabs.com/v1/batch
|
||||
```
|
||||
6. Finally, log into your Google Analytics account and verify that the events were delivered. In your Google Analytics account, navigate to *RealTime** -> **Events**. The RealTime view is important because some dashboards can take one to two days to refresh.
|
||||
|
||||
### Optional: Setting up the open source control plane
|
||||
|
||||
RudderStack's core architecture contains two major components: the data plane and the control plane. The data plane, [rudder-server][20], delivers your event data, and the RudderStack hosted control plane manages the configuration of your sources and destinations.
|
||||
RudderStack's core architecture contains two major components: the data plane and the control plane. The data plane, [rudder-server][29], delivers your event data, and the RudderStack hosted control plane manages the configuration of your sources and destinations.
|
||||
|
||||
However, if you want to manage the source and destination configurations locally, you can set an open source control plane in your environment using the RudderStack Config Generator. (You must have [Node.js][21] installed on your system to use it.)
|
||||
However, if you want to manage the source and destination configurations locally, you can set an open source control plane in your environment using the RudderStack Config Generator. (You must have [Node.js][30] installed on your system to use it.)
|
||||
|
||||
Here are the steps to set up the control plane:
|
||||
|
||||
1. Install and set up RudderStack on the platform of your choice by following the instructions above.
|
||||
2. Run the following commands in this order:
|
||||
* `cd utils/config-gen`
|
||||
* `npm install`
|
||||
* `npm start`
|
||||
|
||||
|
||||
1. Install and set up RudderStack on the platform of your choice by following the instructions above.
|
||||
2. Run the following commands in this order:
|
||||
```
|
||||
cd utils/config-gen
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
You should now be able to access the open source control plane at `http://localhost:3000` by default. If your setup is successful, you will see the user interface.
|
||||
|
||||
![RudderStack open source control plane][22]
|
||||
![RudderStack open source control plane][31]
|
||||
|
||||
(RudderStack, [CC BY-SA 4.0][6])
|
||||
|
||||
To export the existing workspace configuration from the RudderStack-hosted control plane and have RudderStack use it, consult the [docs][23].
|
||||
To export the existing workspace configuration from the RudderStack-hosted control plane and have RudderStack use it, consult the [docs][32].
|
||||
|
||||
### RudderStack and open source
|
||||
|
||||
The core of RudderStack is in the [rudder-server][20] repository. It is open source, licensed under [AGPL-3.0][24]. A majority of the destination integrations live in the [rudder-transformer][25] repository. They are open source as well, licensed under the [MIT License][26]. The SDKs and instrumentation repositories, several tool and utility repositories, and even some [dbt][27] model repositories for use-cases like customer journey analysis and sessionization for the data residing in your data warehouse are open source, licensed under the MIT License, and available in the [GitHub repository][28].
|
||||
The core of RudderStack is in the [rudder-server][33] repository. It is open source, licensed under [AGPL-3.0][34]. A majority of the destination integrations live in the [rudder-transformer][35] repository. They are open source as well, licensed under the [MIT License][36]. The SDKs and instrumentation repositories, several tool and utility repositories, and even some [dbt][37] model repositories for use-cases like customer journey analysis and sessionization for the data residing in your data warehouse are open source, licensed under the MIT License, and available in the [GitHub repository][38].
|
||||
|
||||
You can use RudderStack's open source offering, rudder-server, on your platform of choice. There are setup guides for [Docker][29], [Kubernetes][30], [native installation][31], and [developer machines][32].
|
||||
You can use RudderStack's open source offering, rudder-server, on your platform of choice. There are setup guides for [Docker][39], [Kubernetes][40], [native installation][41], and [developer machines][42].
|
||||
|
||||
RudderStack open source offers:
|
||||
|
||||
1. RudderStack event stream
|
||||
2. 15+ SDKs and source integrations to ingest event data
|
||||
3. 80+ destination and warehouse integrations
|
||||
4. Slack community support
|
||||
|
||||
|
||||
1. RudderStack event stream
|
||||
2. 15+ SDKs and source integrations to ingest event data
|
||||
3. 80+ destination and warehouse integrations
|
||||
4. Slack community support
|
||||
|
||||
#### RudderStack Cloud
|
||||
|
||||
RudderStack also offers a managed option, [RudderStack Cloud][33]. It is fast, reliable, and highly scalable with a multi-node architecture and sophisticated error-handling mechanism. You can hit peak event volume without worrying about downtime, loss of events, or latency.
|
||||
RudderStack also offers a managed option, [RudderStack Cloud][43]. It is fast, reliable, and highly scalable with a multi-node architecture and sophisticated error-handling mechanism. You can hit peak event volume without worrying about downtime, loss of events, or latency.
|
||||
|
||||
Explore our open source repos on [GitHub][28], subscribe to [our blog][34], and follow us on social media: [Twitter][35], [LinkedIn][36], [dev.to][37], [Medium][38], and [YouTube][39]!
|
||||
Image By: (RudderStack, CC BY-SA 4.0)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/3/rudderstack-customer-data-platform
|
||||
|
||||
作者:[Amey Varangaonkar][a]
|
||||
选题:[lujun9972][b]
|
||||
选题:[lkxed][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ameypv
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/data_metrics_analytics_desktop_laptop.png?itok=9QXd7AUr (Person standing in front of a giant computer screen with numbers, data)
|
||||
[b]: https://github.com/lkxed
|
||||
[1]: https://opensource.com/sites/default/files/lead-images/data_metrics_analytics_desktop_laptop.png
|
||||
[2]: https://rudderstack.com/
|
||||
[3]: https://segment.com/
|
||||
[4]: https://app.rudderstack.com/
|
||||
[5]: https://opensource.com/sites/default/files/uploads/rudderstack_login.png (RudderStack login screen)
|
||||
[6]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[7]: https://opensource.com/sites/default/files/uploads/rudderstack_workspace-token.png (RudderStack workspace token)
|
||||
[8]: https://opensource.com/life/16/7/stumbling-git
|
||||
[9]: https://helm.sh/
|
||||
[10]: https://kubernetes.io/docs/tasks/tools/install-kubectl/
|
||||
[11]: https://helm.sh/docs/intro/install/
|
||||
[12]: tmp.AhGpFIyrbZ#token
|
||||
[7]: https://opensource.com/sites/default/files/uploads/rudderstack_login.png
|
||||
[8]: https://opensource.com/sites/default/files/uploads/rudderstack_workspace-token.png
|
||||
[9]: https://opensource.com/life/16/7/stumbling-git
|
||||
[10]: https://helm.sh/
|
||||
[11]: https://kubernetes.io/docs/tasks/tools/install-kubectl/
|
||||
[12]: https://helm.sh/docs/intro/install/
|
||||
[13]: https://github.com/rudderlabs/rudderstack-helm
|
||||
[14]: https://docs.rudderstack.com/installing-and-setting-up-rudderstack/kubernetes
|
||||
[15]: https://raw.githubusercontent.com/rudderlabs/rudder-server/master/rudder-docker.yml
|
||||
[16]: https://docs.rudderstack.com/get-started/adding-source-and-destination-rudderstack
|
||||
[17]: https://docs.rudderstack.com/rudderstack-sdk-integration-guides/rudderstack-javascript-sdk
|
||||
[18]: https://opensource.com/sites/default/files/uploads/googleanalyticstrackingid.png (Google Analytics tracking ID)
|
||||
[19]: https://opensource.com/sites/default/files/uploads/rudderstack_sourcewritekey.png (RudderStack source write key)
|
||||
[20]: https://github.com/rudderlabs/rudder-server
|
||||
[21]: https://nodejs.org/en/download/
|
||||
[22]: https://opensource.com/sites/default/files/uploads/rudderstack_controlplane.png (RudderStack open source control plane)
|
||||
[23]: https://docs.rudderstack.com/how-to-guides/rudderstack-config-generator
|
||||
[24]: https://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
[25]: https://github.com/rudderlabs/rudder-transformer
|
||||
[26]: https://opensource.org/licenses/MIT
|
||||
[27]: https://www.getdbt.com/
|
||||
[28]: https://github.com/rudderlabs
|
||||
[29]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/docker
|
||||
[30]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/kubernetes
|
||||
[31]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/native-installation
|
||||
[32]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/developer-machine-setup
|
||||
[33]: https://resources.rudderstack.com/rudderstack-cloud
|
||||
[34]: https://rudderstack.com/blog/
|
||||
[35]: https://twitter.com/RudderStack
|
||||
[36]: https://www.linkedin.com/company/rudderlabs/
|
||||
[37]: https://dev.to/rudderstack
|
||||
[38]: https://rudderstack.medium.com/
|
||||
[39]: https://www.youtube.com/channel/UCgV-B77bV_-LOmKYHw8jvBw
|
||||
[16]: https://app.rudderstack.com/
|
||||
[17]: https://docs.rudderstack.com/get-started/adding-source-and-destination-rudderstack
|
||||
[18]: https://docs.rudderstack.com/rudderstack-sdk-integration-guides/rudderstack-javascript-sdk
|
||||
[20]: https://docs.rudderstack.com/get-started/adding-source-and-destination-rudderstack
|
||||
[21]: https://docs.rudderstack.com/rudderstack-sdk-integration-guides/rudderstack-javascript-sdk
|
||||
[24]: https://docs.rudderstack.com/get-started/adding-source-and-destination-rudderstack
|
||||
[25]: https://docs.rudderstack.com/rudderstack-sdk-integration-guides/rudderstack-javascript-sdk
|
||||
[27]: https://opensource.com/sites/default/files/uploads/googleanalyticstrackingid.png
|
||||
[28]: https://opensource.com/sites/default/files/uploads/rudderstack_sourcewritekey.png
|
||||
[29]: https://github.com/rudderlabs/rudder-server
|
||||
[30]: https://nodejs.org/en/download/
|
||||
[31]: https://opensource.com/sites/default/files/uploads/rudderstack_controlplane.png
|
||||
[32]: https://docs.rudderstack.com/how-to-guides/rudderstack-config-generator
|
||||
[33]: https://github.com/rudderlabs/rudder-server
|
||||
[34]: https://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
[35]: https://github.com/rudderlabs/rudder-transformer
|
||||
[36]: https://opensource.org/licenses/MIT
|
||||
[37]: https://www.getdbt.com/
|
||||
[38]: https://github.com/rudderlabs
|
||||
[39]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/docker
|
||||
[40]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/kubernetes
|
||||
[41]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/native-installation
|
||||
[42]: https://docs.rudderstack.com/get-started/installing-and-setting-up-rudderstack/developer-machine-setup
|
||||
[43]: https://resources.rudderstack.com/rudderstack-cloud
|
||||
|
Loading…
Reference in New Issue
Block a user