Repository: OpenScan-org/OpenScan2 Branch: main Commit: 373635007c78 Files: 31 Total size: 1.4 MB Directory structure: gitextract_v3pj__kx/ ├── .github/ │ └── FUNDING.yml ├── 2024-10 ReCap.md ├── LICENSE ├── README.md ├── mkdocs.yml ├── software_dev_outline.md └── update/ ├── beta/ │ ├── Arducam.py │ ├── OpenScan.py │ ├── config.txt │ ├── fla.py │ ├── flows.json │ └── settings.js ├── betaArdu/ │ ├── Arducam.py │ ├── OpenScan.py │ ├── config.txt │ ├── fla.py │ ├── flows.json │ └── settings.js ├── main/ │ ├── Arducam.py │ ├── OpenScan.py │ ├── config.txt │ ├── fla.py │ ├── flows.json │ └── settings.js ├── mini/ │ ├── Arducam.py │ ├── OpenScan.py │ ├── config.txt │ ├── fla.py │ ├── flows.json │ └── settings.js └── update.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: OpenScan open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: 2024-10 ReCap.md ================================================ # OpenScan - Bringing 3D scanning to the masses - what was, what is and what can be? ***In the following sections, the OpenScan project is outlined. It is important to understand the background and the bumpy ride that brought us here in order to lay a foundation for future developments*** ___ ___ ## Background Back in 2017, I was looking for an accessible 3D scanning solution and quickly realised, that there was none. All existing solutions either came with a hefty price tag or were not able to create decent 3D scans. I stumbled upon photogrammetry as a very powerful tool and played around with it as it only requires a camera. Refining the lighting and capture settings, I was able to get very decent scans. When I was able to copy both my mailbox as well as my "security" home key, I realized that photogrammetry can yield great results. Being a lazy person, I started to automate the process using an *Arduino Nano*, which was tilting and rotating the object. Adding a ringlight to the camera for better lighting and most of the capture process was automated. It is important to state, that I had NO background in electronics, programming or community management. Every of those skills (and many others) needed to evolve during the development process. **Nothing would have been possible without the immense support and contribution from the very open-minded community. Having so many useful resources publicly available has always been a major cornerstone of the project. For this and many other reasons, being open-source is a core value of the project. Becoming able to contribute to the open-source movement is such an amazing honor!** In 2019, I changed the plattform in favor of the *Raspberry Pi*. This step came with new challenges but also a lot of new options to increase the scanners capabilities. My main goal was to **automate and simplify as much of the photogrammetry capture process as much as possible - maybe even create a one-click-3d-scanning-solution.** In 2021, the optional *OpenScan Cloud* was launched. This lowered the entry barrier even further, as many users either did not want or could not do the processing locally (as this always requires some software knowledge as well as a capable computer). Thanks to the support through Patreon & BuyMeACoffee, the OpenScan Cloud maintains its state till today. Early 2022 Arducam released their *16mp IMX519* camera with focus control. This allowed an even finer control of the scanning process. Adding *focus stacking* to the process increased the quality of the resulting 3D models even further. To this point, the OpenScan project grew on various channels and reached people in over 70 countries. Unfortunately, the evolution of the project created an almost unmanageable pile of documents/codes/files across various platforms and github repositories. There have been several attempts of restructuring and reorganizing the whole structure, but by this point, the sheer amount of data has been totally overwhelming for me. Producing and distributing scanner kits as a business brought its own amount of challenges (customer support, accounting, production, delivery bottlenecks, legal issues ...). By this time, i felt almost incapable of further developing the project and almost abandonned it completely. **Fortunately, there have been several idealistic and very eager community members (especially on the OpenScan Discord), which not only welcomed new OpenScan users and patiently helped with all the existing and known issues of the system. But they also started several community developments, which solved many of the existing hardware and software issues.** ___ ___ ## Current State of the project | great | not so great | | -- | -- | |- modularity
- wide user base across all continents
- used for archeology, dental, research, creation of gaming assets, reverse engineering, miniatures
- the only open-source and low-cost 3D scanning solution
- high quality 3D models
- a lot of potential
- great understanding of the underlying principles and available knowledge |- disconnect between official and community
- many (known) issues in the offical versions --> bad user experience
- relatively high effort needed to get started
- scattered and outdated documentation
- no organizational structure
- no structure for contribution and appreciation thereof | ___ ### Hardware | | OpenScan Mini V1 | OpenScan Classic | OpenScan Mini V2 | OpenScan Midi | | -- | -- | -- | -- | -- | | **state** | official | official | community | community | | **more details** | [github](https://openscan-org.github.io/OpenScan-Doc/hardware/OpenScanMini/) | [github](https://openscan-org.github.io/OpenScan-Doc/hardware/OpenScanClassic/)| [github](https://github.com/OpenScan-org/OpenScan-Design/tree/main?tab=readme-ov-file#openscan-mini-v2) | [github](https://openscan-org.github.io/OpenScan-Doc/hardware/OpenScanClassic/) | ___ ### Firmware/Software | | Official Firmware | "Patreon Beta" | OpenScan Meanwhile | OpenScan Composer | -- | -- | -- | -- | -- | | **state** | official | partly official | community | community | | **more details** | [github](https://openscan-org.github.io/OpenScan-Doc/firmware/setup/) | [patreon (free)](https://www.patreon.com/posts/beta-firmware-2-86937106) | [github](https://github.com/stealthizer/OpenScan2/tree/2024-1o)
[roadmap](https://miro.com/app/board/uXjVNrJGlbQ=/) | [OpenScanComposer.com](https://www.openscancomposer.com/)| ___ ### Community | Channel | Focus | | -- | --| | [Discord](https://discord.gg/gpaKWPpWtG) | - community support
- coordination of development | | [OpenScan.eu](http://openscan.eu) | - official website
- entry point for most new users | | [reddit/r/OpenScan](https://www.reddit.com/r/OpenScan/) | - community support
- show and tell| ___ ### Electronics There are two core components: * The **pi shield** directly connects on top of the Raspberry Pi and is used to interfer with the motors, camera and lighting. * The **ringlight module** for standard pi camera form factor cameras allows for optimal illumination. It is noteworthy, that these two PCBs are not strictly necessary to build a 3D scanner, but greatly simplifies the overall process. ___ #### Pi Shield | | Green Shield (pre-soldered) | Green Shield (solder yourself) | Black Shield | 4 Axis Shield | | -- | -- | -- | -- | -- | | **state** | official | official | community | unpublished | | **more details** | link | link | link | link | ___ #### Ringlight ___ ### OpenScanCloud The [OpenScanCloud](https://github.com/OpenScan-org/OpenScanCloud) is a free and donation-based online photogrammetry processing pipeline with increasing popularity. Its simplicity (one-click) allows users to avoid the need for local processing power and knowledge of a dedicated software. **It is solely financed through Patreon donations and there will never be any kind of monetization crippling its functionality!** - automated focus stacking (though it is not documented at all), when using the openscan firmware - ~0.5TB of Data per month - minor issues with data transmission - hard limitation to 2GB max filesize - manual access token creation and user management - missing documentation - rudimentary windows uploader and python script ___ ### OpenScanBenchy An approach to create a recognizable benchmark for small object 3d scanners. Details on [github](https://github.com/OpenScanEu/OpenScanBenchy/tree/main) ___ ___ ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ # OpenScan2 - 3D Scanner ## Related and more specific repositories If you want to take part in the development of a specific part of the OpenScan system, feel free to join: * [OpenScanCloud - Web API for photogrammetry processing of image files](https://github.com/OpenScanEu/OpenScanCloud) * [OpenScan-Design - 3D printable files and other design approaches](https://github.com/OpenScanEu/OpenScan-Design) * [OpenScan-PCB - A place to discuss and improve the PCB designs](https://github.com/OpenScanEu/OpenScan-PCB) * [OpenScan-ML - Development of new tools using Machine Learning](https://github.com/OpenScanEu/OpenScan-ML) ## Contribution and contributors The project is based on the contribution of many great and open-minded people by doing tutorials on Youtube, comments on Reddit, publications on GitHub and many other places. Without all those voluntary contributors, this project would not be possible at all. Please feel free to join the discussions and development preferably in this repository or on [r/OpenScan](https://www.reddit.com/r/OpenScan/), [Facebook - LowBudget3DScan](https://www.facebook.com/groups/142108429832711) or [OpenScan.eu/forum](https://openscan.eu/forum) Thank you! ================================================ FILE: mkdocs.yml ================================================ site_name: OpenScan plugins: - autolinks - search: lang: - en theme: name: material language: en palette: primary: teal accent: indigo features: - content.code.annotate - search.suggest - search.highlight markdown_extensions: - pymdownx.highlight: anchor_linenums: true - pymdownx.inlinehilite - pymdownx.snippets - pymdownx.emoji: emoji_index: !!python/name:materialx.emoji.twemoji emoji_generator: !!python/name:materialx.emoji.to_svg - attr_list - md_in_html - admonition - pymdownx.details - pymdownx.superfences - pymdownx.tabbed: alternate_style: true - pymdownx.critic - pymdownx.caret - pymdownx.keys - pymdownx.mark - pymdownx.tilde - def_list - pymdownx.tasklist: custom_checkbox: true nav: - Introduction: "index.md" - Builds: - Classic: "builds/OpenScanClassic.md" - Mini: "builds/OpenScanMini.md" - PCB: "builds/PCBs.md" - Firmware: - Setup: "firmware/setup.md" - Usage: "firmware/usage.md" - Photogrammetry: - Basics: "photogrammetry/basics.md" - Software: "photogrammetry/software.md" - Changelog: "changelog.md" ================================================ FILE: software_dev_outline.md ================================================ Based on [OpenScan3](https://github.com/OpenScan-org/OpenScan3) ## 1. Core Infrastructure & Setup - [ ] Update initial setup procedure - [x] Basic FastAPI application structure setup (already done in app-main.py) - [ ] Configure settings for different hardware setups (greenshield, blackshield, grblHAL) - [ ] Set up error handling and logging system - [ ] Implement Network security - [ ] Use virtual environment - [ ] Integrate ramdisk for faster temporary file handling ### 1.1. Network & Connectivity - [ ] Add hotspot mode - [ ] Add wifi configuration & testing - [ ] Add test for internet connectivity ## 2. Hardware Control Components ### 2.1. Camera System - [ ] Review and update camera controllers (gphoto2, v4l2, picamera2) - [ ] Implement unified camera interface - [ ] Implement camera settings management - [ ] Integrate Focus control system: - [ ] Integrate software controlled focus - [ ] Add mechanic focus through third motor ### 2.2. Motor Control System - [ ] Migrate motor controllers from old system (directly through GPIO) - [ ] Add Motor controller through GRBLhal - [ ] Integrate optional endstops - [ ] Implement motor movement coordination - [ ] Add tests for users ### 2.3. Light Control system - [ ] Migrate Light controller (directly through GPIO) - [ ] Add Light controller through GRBLhal - [ ] Add tests for users ### 2.4. Fan Control System - [ ] Add fan controller (directly through GPIO) - [ ] Add Fan controller through GRBLhal - [ ] Add Temperature dependent fan control ### 2.5. Other Peripherals ? ## 3. Scanning Logic ### 3.1 Smart Pre-Scan systems (nice to have/optional) - [ ] Add auto-exposure detection based on histogram - [ ] Add auto-crop detection routine - [ ] Add auto-depth detection - [ ] Add Evaluation of object preparation (feature detection) ### 3.2. Core Scanning System - [ ] Add scan templates/presets - [ ] Migrate scanning process controller - [ ] Implement proper scan state management - [ ] Add scan progress tracking - [ ] Add resume from failure point - [ ] Implement pause/resume functionality ### 3.3. Path Generation - [ ] Migrate different scanning patterns (Grid, Fibonacci, Spiral, Archimedes) - [ ] Implement path optimization - [ ] Add path visualization support ### 4. Scan Project Handling - [ ] Implement project creation and management - [ ] Implement proper file structure for projects - [ ] Add External drive for saving - [ ] Add network drive for saving - [ ] Add project metadata handling - [ ] Add download project - [ ] Add delete project - [ ] Add delete all projects - [ ] Add merge projects - [ ] Add scan meta data (positions, focus, resolution, timestamps) ## 5. Processing Integration ### 5.1. OpenScan Cloud - [ ] Migrate cloud upload functionality - [ ] Implement secure authentication - [ ] Add progress tracking for uploads - [ ] Implement download functionality ### 5.2. create Project files for other programs - [ ] Metashape - [ ] Reality Capture - [ ] 3DF Zephyr - [ ] Meshroom ## 6. System Services - [ ] Migrate system status monitoring - [ ] Implement proper shutdown/reboot handlers - [ ] Add system health checks - [ ] Add system statistics - [ ] Add Diskspace monitoring - [ ] Add Diskspace warnings - [ ] Implement update service - [ ] Implement update versioning - [ ] Add Change Log - [ ] Add Server message service - [ ] Add Samba client - [ ] Add SSH on/off ## 7. Testing ?? - [ ] Set up unit testing infrastructure - [ ] Add integration tests - [ ] Implement hardware simulation for testing - [ ] Add CI/CD pipeline ## 8. Documentation - [ ] API documentation - [ ] System architecture documentation - [ ] Hardware setup documentation - [ ] User guide ================================================ FILE: update/beta/Arducam.py ================================================ import time import os try: import v4l2 except Exception as e: print(e) print("Try to install v4l2-fix") try: from pip import main as pipmain except ImportError: from pip._internal import main as pipmain pipmain(['install', 'v4l2-fix']) print("\nTry to run the focus program again.") exit(0) import fcntl import errno # # Type # v4l2.V4L2_CTRL_TYPE_INTEGER # v4l2.V4L2_CTRL_TYPE_BOOLEAN # v4l2.V4L2_CTRL_TYPE_MENU # v4l2.V4L2_CTRL_TYPE_BUTTON # v4l2.V4L2_CTRL_TYPE_INTEGER64 # v4l2.V4L2_CTRL_TYPE_CTRL_CLASS # # Flags # v4l2.V4L2_CTRL_FLAG_DISABLED # v4l2.V4L2_CTRL_FLAG_GRABBED # v4l2.V4L2_CTRL_FLAG_READ_ONLY # v4l2.V4L2_CTRL_FLAG_UPDATE # v4l2.V4L2_CTRL_FLAG_INACTIVE # v4l2.V4L2_CTRL_FLAG_SLIDER def assert_valid_queryctrl(queryctrl): return queryctrl.type & ( v4l2.V4L2_CTRL_TYPE_INTEGER | v4l2.V4L2_CTRL_TYPE_BOOLEAN | v4l2.V4L2_CTRL_TYPE_MENU | v4l2.V4L2_CTRL_TYPE_BUTTON | v4l2.V4L2_CTRL_TYPE_INTEGER64 | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS | 7 | 8 | 9 ) and queryctrl.flags & ( v4l2.V4L2_CTRL_FLAG_DISABLED | v4l2.V4L2_CTRL_FLAG_GRABBED | v4l2.V4L2_CTRL_FLAG_READ_ONLY | v4l2.V4L2_CTRL_FLAG_UPDATE | v4l2.V4L2_CTRL_FLAG_INACTIVE | v4l2.V4L2_CTRL_FLAG_SLIDER ) def get_device_controls_menu(fd, queryctrl): querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum) while querymenu.index <= queryctrl.maximum: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu) yield querymenu querymenu.index += 1 def get_device_controls_by_class(fd, control_class): # enumeration by control class queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) while True: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) except IOError as e: assert e.errno == errno.EINVAL break if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class: break yield queryctrl queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) def getdict(struct): val = dict((field, getattr(struct, field)) for field, _ in struct._fields_) val.pop("reserved") return val def get_device_controls(fd): # original enumeration method queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE) while queryctrl.id < v4l2.V4L2_CID_LASTP1: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) print(queryctrl.name) except IOError as e: # this predefined control is not supported by this device assert e.errno == errno.EINVAL queryctrl.id += 1 continue queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1) def get_ctrls(vd): ctrls = [] # enumeration by control class for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA): for queryctrl in get_device_controls_by_class(vd, class_): ctrl = getdict(queryctrl) if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): # print(querymenu.name) ctrl["menu"].append(querymenu.name) if queryctrl.type == 9: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): ctrl["menu"].append(querymenu.index) ctrls.append(ctrl) return ctrls def set_ctrl(vd, id, value): ctrl = v4l2.v4l2_control() ctrl.id = id ctrl.value = value try: fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl) except IOError as e: print(e) def get_ctrl(vd, id): ctrl = v4l2.v4l2_control() ctrl.id = id try: fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl) except IOError as e: print(e) return None return ctrl.value class Focuser: FOCUS_ID = 0x009a090a dev = None def __init__(self, dev=0): self.focus_value = 0 self.dev = dev if type(dev) == int or (type(dev) == str and dev.isnumeric()): self.dev = "/dev/video{}".format(dev) self.fd = open(self.dev, 'r') self.ctrls = get_ctrls(self.fd) self.hasFocus = False for ctrl in self.ctrls: if ctrl['id'] == Focuser.FOCUS_ID: self.hasFocus = True self.opts[Focuser.OPT_FOCUS]["MIN_VALUE"] = ctrl['minimum'] self.opts[Focuser.OPT_FOCUS]["MAX_VALUE"] = ctrl['maximum'] self.opts[Focuser.OPT_FOCUS]["DEF_VALUE"] = ctrl['default'] self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID) if not self.hasFocus: raise RuntimeError("Device {} has no focus_absolute control.".format(self.dev)) def read(self): return self.focus_value def write(self, value): self.focus_value = value # os.system("v4l2-ctl -d {} -c focus_absolute={}".format(self.dev, value)) set_ctrl(self.fd, Focuser.FOCUS_ID, value) OPT_BASE = 0x1000 OPT_FOCUS = OPT_BASE | 0x01 OPT_ZOOM = OPT_BASE | 0x02 OPT_MOTOR_X = OPT_BASE | 0x03 OPT_MOTOR_Y = OPT_BASE | 0x04 OPT_IRCUT = OPT_BASE | 0x05 opts = { OPT_FOCUS : { "MIN_VALUE": 0, "MAX_VALUE": 1000, "DEF_VALUE": 0, }, } def reset(self,opt,flag = 1): info = self.opts[opt] if info == None or info["DEF_VALUE"] == None: return self.set(opt,info["DEF_VALUE"]) def get(self,opt,flag = 0): info = self.opts[opt] return self.read() def set(self,opt,value,flag = 1): info = self.opts[opt] if value > info["MAX_VALUE"]: value = info["MAX_VALUE"] elif value < info["MIN_VALUE"]: value = info["MIN_VALUE"] self.write(value) print("write: {}".format(value)) def __del__(self): self.fd.close() pass ================================================ FILE: update/beta/OpenScan.py ================================================ basepath = '/home/pi/OpenScan/' from os.path import isfile def load_bool(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') if value == '1' or value == 'True' or value =='true': value = True else: value = False return value def load_str(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') return value def load_int(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = int(file.read().replace('\n','')) return value def load_float(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = float(file.read().replace('\n','')) return value def save(name, value): filename = basepath+'settings/'+name with open(filename, 'w+') as file: file.write(str(value)) return def OpenScanCloud(cmd, msg): from requests import get osc_user = 'openscan' osc_pw = 'free' osc_server = 'http://openscanfeedback.dnsuser.de:1334/' try: r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg) except: r = type('obj', (object,), {'status_code' : 404, 'text':None}) return r def camera(cmd, msg = {}): from requests import get flask = 'http://127.0.0.1:1312/' try: r = get(flask + cmd, params=msg) return r.status_code except: return 400 def motorrun(motor,angle,ES_enable=False,ES_start_state = True): #motor can be "rotor", "tt" or "extra" import RPi.GPIO as GPIO from time import sleep from math import cos msg = {'cmd':'set'} camera('/ping', msg) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) spr = load_int(motor + '_stepsperrotation') dirpin = load_int('pin_' + motor + '_dir') steppin = load_int('pin_' + motor +'_step') ES_pin = load_int('pin_' + motor + '_endstop') dir = load_int(motor + '_dir') ramp = load_int(motor + '_accramp') acc = load_float(motor + '_acc') delay_init = load_float(motor + '_delay') delay = delay_init step_count=int(angle*spr/360) * dir GPIO.setup(dirpin, GPIO.OUT) GPIO.setup(steppin, GPIO.OUT) GPIO.setup(ES_pin, GPIO.IN, pull_up_down = GPIO.PUD_UP) if (step_count>0): GPIO.output(dirpin, GPIO.HIGH) if(step_count<0): GPIO.output(dirpin, GPIO.LOW) step_count=-step_count for x in range(step_count): if ES_enable == True and GPIO.input(ES_pin) != ES_start_state: break GPIO.output(steppin, GPIO.HIGH) if x<=ramp and x<=step_count/2: delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc) #delay=delay_init+(ramp-x)*(delay_init)/acc elif step_count-x<=ramp and x>step_count/2: delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc) #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc else: delay = delay_init sleep(delay) GPIO.output(steppin, GPIO.LOW) sleep(delay) def ringlight(number,state): import RPi.GPIO as GPIO msg = {'cmd':'set'} camera('/ping', msg) pin = load_int('pin_ringlight' + str(number)) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, state) def take_photo(file): from os import system filepath = basepath + file model=load_str('model') shutter = str(load_int('cam_shutter')) saturation = load_str('cam_saturation') contrast = load_str('cam_contrast') awbg_red = load_str('cam_awbg_red') awbg_blue = load_str('cam_awbg_blue') gain = load_str('cam_gain') quality = load_int('cam_jpeg_quality') filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg' #width = load_str('cam_resx') #height = load_str('cam_resy') timeout = load_str('cam_timeout') cropx = load_int('cam_cropx')/200 cropy = load_int('cam_cropy')/200 rotation = load_int('cam_rotation') AF = load_bool('cam_AFmode') camera = load_str('camera') if camera == 'imx519' and AF == True: autofocus = ' --autofocus ' else: autofocus = '' if camera == "usb_webcam": cmd = 'fswebcam -i 0 -r "1280x720" -F 5 --no-banner --jpeg 95 --save ' + filepath2 else: cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1' # cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus system(cmd) return cmd def get_points(samples=1): from math import pi, sqrt, acos, atan2, cos, sin points = [] phi = pi * (3. - sqrt(5.)) for i in range(int(samples)): y = 1 - (i / float(samples - 1)) * 2 radius = sqrt(1 - y * y) theta = phi * i x = cos(theta) * radius z = sin(theta) * radius r=sqrt(x*x+y*y+z*z) theta_neu=acos(z/r)*180/pi phi_neu=atan2(y,x)*180/pi points.append((theta_neu-90,phi_neu)) points.sort() return points def create_coordinates(angle_min, angle_max,point_count): point_count_final=point_count if angle_max < angle_min: a = angle_min angle_min = angle_max angle_max = a point_count=point_count*90/(angle_max-angle_min) actual_points=0 while actual_pointsangle_min and x20: point_count=point_count+3 else: point_count=point_count+1 return filtered ================================================ FILE: update/beta/config.txt ================================================ # For more options and information see # http://rpf.io/configtxt # Some settings may impact device functionality. See link above for details # uncomment if you get no picture on HDMI for a default "safe" mode #hdmi_safe=1 hdmi_blanking=2 # uncomment the following to adjust overscan. Use positive numbers if console # goes off screen, and negative if there is too much border #overscan_left=16 #overscan_right=16 #overscan_top=16 #overscan_bottom=16 # uncomment to force a console size. By default it will be display's size minus # overscan. #framebuffer_width=1280 #framebuffer_height=720 # uncomment if hdmi display is not detected and composite is being output #hdmi_force_hotplug=1 # uncomment to force a specific HDMI mode (this will force VGA) #hdmi_group=1 #hdmi_mode=1 # uncomment to force a HDMI mode rather than DVI. This can make audio work in # DMT (computer monitor) modes #hdmi_drive=2 # uncomment to increase signal to HDMI, if you have interference, blanking, or # no display #config_hdmi_boost=4 # uncomment for composite PAL #sdtv_mode=2 #uncomment to overclock the arm. 700 MHz is the default. #arm_freq=800 # Uncomment some or all of these to enable the optional hardware interfaces #dtparam=i2c_arm=on #dtparam=i2s=on #dtparam=spi=on # Uncomment this to enable infrared communication. #dtoverlay=gpio-ir,gpio_pin=17 #dtoverlay=gpio-ir-tx,gpio_pin=18 # Additional overlays and parameters are documented /boot/overlays/README # Enable audio (loads snd_bcm2835) dtparam=audio=on # Automatically load overlays for detected cameras camera_auto_detect=0 # Automatically load overlays for detected DSI displays display_auto_detect=1 # Enable DRM VC4 V3D driver #dtoverlay=vc4-kms-v3d max_framebuffers=2 # Disable compensation for displays with overscan disable_overscan=1 [cm4] # Enable host mode on the 2711 built-in XHCI USB controller. # This line should be removed if the legacy DWC2 controller is required # (e.g. for USB device mode) or if USB support is not required. otg_mode=1 [pi4] # Run as fast as firmware / board allows arm_boost=1 [all] camera_auto_detect=0 gpu_mem=256 dtoverlay=vc4-fkms-v3d dtoverlay=imx519,media-controller=1 ================================================ FILE: update/beta/fla.py ================================================ from flask import Flask, make_response, jsonify, request, abort from PIL import Image import gphoto2 as gp from time import sleep, time import shutil from OpenScan import load_int, load_float, load_bool, ringlight import RPi.GPIO as GPIO from math import sqrt import os GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) app = Flask(__name__) basedir = '/home/pi/OpenScan/' timer = time() ################################################################################################################### @app.route('/shutdown', methods=['get']) def shutdown(): delay = 0.1 ringlight(2,False) for i in range (5): ringlight(1,True) sleep(delay) ringlight(1,False) sleep(delay) os.system('shutdown -h now') ################################################################################################################### @app.route('/reboot', methods=['get']) def reboot(): delay = 0.1 ringlight(2,False) for i in range (5): ringlight(1,True) sleep(delay) ringlight(1,False) sleep(delay) os.system('reboot -h') ################################################################################################################### @app.route('/ping', methods=['get']) def ping(): global timer cmd = str(request.args.get('cmd')) if cmd == 'set': timer = time() inactive = time() - timer return ({'inactive':inactive}, 200) ################################################################################################################### @app.route('/gphoto_init', methods=['get']) def gphoto_init(): global camera camera = gp.Camera() camera.init() return ({}, 200) ################################################################################################################### @app.route('/gphoto_preview', methods=['get']) def gphoto_preview(): filepath = str(request.args.get('filepath')) camera_file = gp.gp_camera_capture_preview(camera)[1] target = basedir + filepath camera_file.save(target) return ({}, 200) ################################################################################################################### @app.route('/gphoto_capture', methods=['get']) def gphoto_capture(): filepath = str(request.args.get('filepath')) file_path = camera.capture(gp.GP_CAPTURE_IMAGE) camera_file = camera.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL) camera_file.save(basedir + filepath) return ({}, 200) ################################################################################################################### @app.route('/gphoto_test', methods=['get']) def gphoto_test(): text = camera.get_summary() return ({}, 200) ################################################################################################################### @app.route('/gphoto_exit', methods=['get']) def gphoto_exit(): global camera camera.exit() return ({}, 200) ################################################################################################################### @app.route('/crop', methods=['get']) def crop(): output_downscale = load_bool('cam_output_downscale') output_resolution = load_int('cam_output_resolution') preview_resolution = load_int('cam_preview_resolution') filepath_in = basedir + str(request.args.get('filepath_in')) filepath_out = basedir + str(request.args.get('filepath_out')) cropx = int(request.args.get('cropx'))/200 cropy = int(request.args.get('cropy'))/200 rotation = int(request.args.get('rotation')) preview = str(request.args.get('preview')) downscale = 1 with Image.open(filepath_in) as img: w,h = img.size if cropx != 0 or cropy != 0: img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy))) if rotation == 90: img = img.transpose(Image.ROTATE_90) elif rotation == 180: img= img.transpose(Image.ROTATE_180) elif rotation == 270: img= img.transpose(Image.ROTATE_270) if preview == "True": w,h = img.size factor = (w*h)/preview_resolution if factor > 1: img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS) elif output_downscale == True: w,h = img.size factor = (w*h)/output_resolution if factor > 1: img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS) img.save(filepath_out, quality=95, subsampling=0) return ({}, 200) ################################################################################################################### @app.route('/external_capture', methods=['get']) def external_capture(): pin = load_int('pin_external') delay_before = load_float('cam_delay_before') timeout = load_float('cam_timeout')/1000 delay_after = load_float('cam_delay_after') GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, GPIO.LOW) sleep(delay_before) GPIO.output(pin, GPIO.HIGH) sleep(timeout) GPIO.output(pin, GPIO.LOW) sleep(delay_after) return ({}, 200) if __name__ == '__main__': # app.run(host='127.0.0.1', port=1312, debug=False, threaded=True) app.run(host='0.0.0.0', port=1312, debug=False, threaded=True) ================================================ FILE: update/beta/flows.json ================================================ [ { "id": "829d803b6033a693", "type": "tab", "label": "HOME", "disabled": false, "info": "", "env": [] }, { "id": "1613373abaf77a2c", "type": "tab", "label": "SCAN", "disabled": false, "info": "", "env": [] }, { "id": "4981d84ef1a366d1", "type": "tab", "label": "Files&Cloud", "disabled": false, "info": "", "env": [] }, { "id": "017bd4e4a428bee5", "type": "tab", "label": "SETTINGS", "disabled": false, "info": "", "env": [] }, { "id": "c8e7ecb5849edb9a", "type": "tab", "label": "UPDATE", "disabled": false, "info": "", "env": [] }, { "id": "b3150b13e34b1fe8", "type": "ui_tab", "name": "OpenScan", "icon": "dashboard", "order": 1, "disabled": false, "hidden": false }, { "id": "b6e9c2df6b28ff66", "type": "ui_base", "theme": { "name": "theme-dark", "lightTheme": { "default": "#0094CE", "baseColor": "#0094CE", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "darkTheme": { "default": "#097479", "baseColor": "#097479", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "customTheme": { "name": "Untitled Theme 1", "default": "#4B7930", "baseColor": "#4B7930", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" }, "themeState": { "base-color": { "default": "#097479", "value": "#097479", "edited": false }, "page-titlebar-backgroundColor": { "value": "#097479", "edited": false }, "page-backgroundColor": { "value": "#111111", "edited": false }, "page-sidebar-backgroundColor": { "value": "#333333", "edited": false }, "group-textColor": { "value": "#0eb8c0", "edited": false }, "group-borderColor": { "value": "#555555", "edited": false }, "group-backgroundColor": { "value": "#333333", "edited": false }, "widget-textColor": { "value": "#eeeeee", "edited": false }, "widget-backgroundColor": { "value": "#097479", "edited": false }, "widget-borderColor": { "value": "#333333", "edited": false }, "base-font": { "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" } }, "angularTheme": { "primary": "indigo", "accents": "blue", "warn": "red", "background": "grey", "palette": "light" } }, "site": { "name": "OpenScan 3D Scanner", "hideToolbar": "false", "allowSwipe": "false", "lockMenu": "false", "allowTempTheme": "true", "dateFormat": "DD/MM/YYYY", "sizes": { "sx": 46, "sy": 46, "gx": 10, "gy": 10, "cx": 6, "cy": 6, "px": 6, "py": 6 } } }, { "id": "729f9ea6e3513c9b", "type": "ui_group", "name": "Home", "tab": "b3150b13e34b1fe8", "order": 2, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "65ae49b64fa0d83e", "type": "ui_tab", "name": "Settings", "icon": "dashboard", "order": 4, "disabled": false, "hidden": false }, { "id": "4fe6b4c0ade0938a", "type": "ui_group", "name": "General", "tab": "65ae49b64fa0d83e", "order": 1, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "0fe66c9190b8a87c", "type": "ui_group", "name": "Network", "tab": "65ae49b64fa0d83e", "order": 2, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "93aadb71dee6d977", "type": "ui_group", "name": "Camera", "tab": "65ae49b64fa0d83e", "order": 4, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "d49a6dfd7fb17096", "type": "ui_group", "name": "Motor", "tab": "65ae49b64fa0d83e", "order": 5, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "644b3bcc903d46ca", "type": "ui_group", "name": "Pinout", "tab": "65ae49b64fa0d83e", "order": 6, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "e23b837a9f040895", "type": "ui_tab", "name": "Scan", "icon": "dashboard", "order": 2, "disabled": false, "hidden": false }, { "id": "7aaf184330605300", "type": "ui_group", "name": "Settings", "tab": "e23b837a9f040895", "order": 1, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "ce9cc9d915dc6eb6", "type": "ui_group", "name": "Picamera", "tab": "e23b837a9f040895", "order": 2, "disp": false, "width": "12", "collapse": false, "className": "" }, { "id": "90223f7ddc082321", "type": "ui_group", "name": "Arducam", "tab": "e23b837a9f040895", "order": 3, "disp": false, "width": 12, "collapse": false, "className": "" }, { "id": "7625f9c9e8dbc5c6", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "", "order": 4, "width": 1, "height": 1 }, { "id": "3b4bd36726be16d5", "type": "ui_group", "name": "OpenScanCloud", "tab": "65ae49b64fa0d83e", "order": 3, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "b5fdd57b.15eda8", "type": "ui_group", "name": "Main", "tab": "15a222ed.d70a7d", "order": 1, "disp": false, "width": 13, "collapse": false }, { "id": "db43d646.2074c8", "type": "ui_group", "name": "OpenScanCloud", "tab": "15a222ed.d70a7d", "order": 2, "disp": true, "width": "6", "collapse": false }, { "id": "15a222ed.d70a7d", "type": "ui_tab", "name": "Files&Cloud", "icon": "dashboard", "order": 3, "disabled": false, "hidden": false }, { "id": "ddbd496e.93a288", "type": "ui_group", "name": "Manage Updates", "tab": "d25e08b4.5b27e8", "order": 1, "disp": true, "width": "6", "collapse": false }, { "id": "3ce32450.e0cffc", "type": "ui_group", "name": "System & Stats", "tab": "d25e08b4.5b27e8", "order": 2, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "d25e08b4.5b27e8", "type": "ui_tab", "name": "Update & Info", "icon": "dashboard", "order": 5, "disabled": false, "hidden": false }, { "id": "1f7f7e1e24f5ad9b", "type": "ui_group", "name": "Initialize", "tab": "b3150b13e34b1fe8", "order": 3, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "5b3e5aca21140e9a", "type": "ui_group", "name": "Update", "tab": "b3150b13e34b1fe8", "order": 1, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "700f47327133ab68", "type": "ui_spacer", "z": "829d803b6033a693", "name": "spacer", "group": "729f9ea6e3513c9b", "order": 6, "width": 1, "height": 1 }, { "id": "ebf828f29201a53b", "type": "ui_spacer", "z": "829d803b6033a693", "name": "spacer", "group": "729f9ea6e3513c9b", "order": 8, "width": 1, "height": 1 }, { "id": "3b4961c4e72ff58a", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "4fe6b4c0ade0938a", "order": 6, "width": 6, "height": 1 }, { "id": "5ef40dca2c6c6aab", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "4fe6b4c0ade0938a", "order": 11, "width": 6, "height": 1 }, { "id": "bdd26746cc1e1ba0", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "3b4bd36726be16d5", "order": 6, "width": 2, "height": 1 }, { "id": "3584b5ef2b7acb72", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "3b4bd36726be16d5", "order": 8, "width": 2, "height": 1 }, { "id": "cac67f0e.f01fa", "type": "ui_group", "name": "Button Top", "tab": "", "order": 1, "disp": true, "width": "6", "collapse": false }, { "id": "b73c392ffd8ca3f2", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "90223f7ddc082321", "order": 14, "width": 2, "height": 1 }, { "id": "89fe04171cd2f35b", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "90223f7ddc082321", "order": 15, "width": 2, "height": 1 }, { "id": "80c9c0059de08f02", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "90223f7ddc082321", "order": 16, "width": 2, "height": 1 }, { "id": "3fe52603e2ac73b6", "type": "ui_template", "z": "829d803b6033a693", "group": "729f9ea6e3513c9b", "name": "Background", "order": 1, "width": 0, "height": 0, "format": "", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "global", "className": "", "x": 110, "y": 40, "wires": [ [] ] }, { "id": "4468f691.103eb8", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 2, "width": 3, "height": 2, "passthru": false, "label": "SCAN", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "1", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 100, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "6560dd25.9e76c4", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 4, "width": 3, "height": 2, "passthru": false, "label": "Settings", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "3", "payloadType": "num", "topic": "", "topicType": "str", "x": 100, "y": 180, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "62cd5288.2805fc", "type": "ui_ui_control", "z": "829d803b6033a693", "name": "", "events": "all", "x": 280, "y": 100, "wires": [ [] ] }, { "id": "71e72293.91c6fc", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 3, "width": 3, "height": 2, "passthru": false, "label": "Files", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "2", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 140, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "e7306ef2.3b4df", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 5, "width": 3, "height": 2, "passthru": false, "label": "Update&Info", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "4", "payloadType": "num", "topic": "", "topicType": "str", "x": 110, "y": 220, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "88edad7ca53698fd", "type": "inject", "z": "829d803b6033a693", "name": "1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "true", "payloadType": "bool", "x": 90, "y": 400, "wires": [ [ "000a811a215e08d4", "83c2b5ea51f0fec3", "88fde4ab78c965d7", "bee62d2a99cbc63b", "8e39e4a037487ecd", "bb84b9e5c7d8e21f", "7113d7b25a851151", "c4c1580c289fc7bd", "7494af94bc76a6a8" ] ] }, { "id": "bd75f33b8a57c522", "type": "link out", "z": "829d803b6033a693", "name": "enable", "mode": "link", "links": [ "8367cfa0bf5bc5df", "92c98e6ce7cd25f9", "b33d604c.5f1a6" ], "x": 335, "y": 440, "wires": [] }, { "id": "000a811a215e08d4", "type": "function", "z": "829d803b6033a693", "name": "enable", "func": "msg.enabled = true\nmsg.payload = 1\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 440, "wires": [ [ "bd75f33b8a57c522" ] ] }, { "id": "83c2b5ea51f0fec3", "type": "function", "z": "829d803b6033a693", "name": "disable", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 240, "y": 480, "wires": [ [ "6b94bf2295b1b31d" ] ] }, { "id": "6b94bf2295b1b31d", "type": "link out", "z": "829d803b6033a693", "name": "disable", "mode": "link", "links": [ "a1d29e56599da0bd" ], "x": 335, "y": 480, "wires": [] }, { "id": "88fde4ab78c965d7", "type": "function", "z": "829d803b6033a693", "name": "write", "func": "var file = 'status_cloud'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\ncontent = 'ready'\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 520, "wires": [ [] ] }, { "id": "960912e90ba5b5bc", "type": "link out", "z": "829d803b6033a693", "name": "started1s", "mode": "link", "links": [ "2f4c0f98.dee2", "397ab7f44b893c89", "65145c939b6647e2", "65b38bfeb3fee710", "6d1e12f51f9af0b6", "788fabff98c7973c", "9b2bc9849aee310b", "a1e14624058e74cd", "a67c18aaca2f5fa5", "bd80ec228fb9a86d", "cc9c4092edeb43cc", "d3fc91d87d5d5f62", "d7c1fb4c028b21a5", "e5f38b4a07a5e278", "f0b355967b33dfee", "d0104e0163745993", "5e7d5e4335d37794" ], "x": 615, "y": 800, "wires": [] }, { "id": "168d72a54504b327", "type": "inject", "z": "829d803b6033a693", "name": "5/0.1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.1", "crontab": "", "once": true, "onceDelay": "5", "topic": "", "payload": "", "payloadType": "str", "x": 100, "y": 720, "wires": [ [ "6c6ef2255a7d39e5" ] ] }, { "id": "6c6ef2255a7d39e5", "type": "link out", "z": "829d803b6033a693", "name": "repeat 5s/0.1s", "mode": "link", "links": [ "61990987acd0f263", "2415272f42ce468c" ], "x": 195, "y": 720, "wires": [] }, { "id": "bee62d2a99cbc63b", "type": "function", "z": "829d803b6033a693", "name": "global", "func": "global.set('flag_pw', true)\nglobal.set('flag', true)\nglobal.set('combine', false)\nglobal.set('focus', 2838)\nglobal.set('focus1', 0)\nglobal.set('focus2', 0)\n\nglobal.set('focuser', true)\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 400, "wires": [ [ "f20da2fc4978b7bf" ] ] }, { "id": "544d20f02215011a", "type": "function", "z": "829d803b6033a693", "name": "CREATE FACTORY DEFAULT", "func": "msg = { \n'overwrite':msg.overwrite,\n'settings':\n {\n 'advanced_settings':false,\n 'cameras':{\n 'imx519':[4656,3496],\n 'imx219':[3280,2464],\n 'imx477':[4056,3040],\n 'ov5647':[2592,1944],\n 'imx378':[3840,2880],\n 'ov9271':[1280,800],\n 'imx290a':[1920,1080],\n 'imx290b':[1920,1080],\n },\n 'cam_AFmode':true,\n 'cam_STmode':true,\n 'cam_stacksize':2,\n 'cam_awbg_blue':0,\n 'cam_awbg_red':0,\n 'cam_contrast':1,\n 'cam_cropx':0,\n 'cam_cropy':0,\n 'cam_delay_after':0,\n 'cam_delay_before':0,\n 'camera':'',\n 'cam_gain':1,\n 'cam_jpeg_quality':95,\n 'cam_rotation':0,\n 'cam_saturation':1,\n 'cam_shutter':50000,\n 'cam_timeout':800,\n 'hostname':'',\n 'model':'',\n 'osc_credit':'',\n 'osc_limit_filesize':'',\n 'osc_limit_photos':'',\n 'osc_splitsize':200000000,\n 'pin_endstop1':24,\n 'pin_endstop2':25,\n 'pin_external':10,\n 'pin_ringlight1':17,\n 'pin_ringlight2':27,\n 'pin_rotor_dir':5,\n 'pin_rotor_enable':23,\n 'pin_rotor_step':6,\n 'pin_tt_dir':9,\n 'pin_tt_enable':22,\n 'pin_tt_step':11,\n 'rotor_acc':1,\n 'rotor_accramp':2000,\n 'rotor_angle':10,\n 'rotor_anglemax':60,\n 'rotor_anglemin':-20,\n 'rotor_anglestart':0,\n 'rotor_delay':0.0001,\n 'rotor_dir':1,\n 'rotor_stepsperrotation':48000,\n 'routine_photocount':50,\n 'routine_projectname':'default',\n 'smb':true,\n 'ssh':true,\n 'status_cloud':'ready',\n 'status_internal_cam':'--READY--',\n 'status_uploadprogress':'',\n 'terms':false,\n 'token':'',\n 'tt_acc':1,\n 'tt_accramp':200,\n 'tt_angle':10,\n 'tt_delay':0.0001,\n 'tt_dir':1,\n 'tt_stepsperrotation':3200,\n 'cam_focus':2838,\n 'cam_focus1':0,\n 'cam_focus2':0,\n 'uploadprogress':'',\n 'update_type':'beta',\n 'update_auto':true,\n 'downscale_threshold':1000,\n 'turntable_mode':false,\n 'timeout_ringlight':300,\n 'diskspace_threshold':4000,\n 'updateable':false,\n 'cam_focuspeak':false,\n 'cam_histogram':false,\n 'routine_secondpass':true,\n 'cam_output_resolution':20000000,\n 'cam_preview_resolution':2000000,\n 'cam_output_downscale':false,\n 'pin_rotor_endstop':24,\n 'pin_tt_endstop':25,\n 'pin_extra_endstop':26,\n 'pin_extra_dir':21,\n 'pin_extra_step':20,\n 'pin_extra_enable':19,\n 'extra_acc':1,\n 'extra_accramp':200,\n 'extra_angle':10,\n 'extra_delay':0.0001,\n 'extra_dir':1,\n 'extra_stepsperrotation':3200,\n}}\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 310, "y": 800, "wires": [ [ "c77552216a8bb781" ] ] }, { "id": "a1f0ed7d5a9d670e", "type": "inject", "z": "829d803b6033a693", "name": "", "props": [ { "p": "overwrite", "v": "false", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "0.1", "topic": "", "x": 90, "y": 800, "wires": [ [ "544d20f02215011a" ] ] }, { "id": "c77552216a8bb781", "type": "python3-function", "z": "829d803b6033a693", "name": "chk files", "func": "from os import listdir\n\nbasedir = '/home/pi/OpenScan/settings/'\n\nmsg['payload'] = ''\n\nfiles = listdir(basedir)\n\nfor i in msg['settings']:\n if msg['overwrite'] != True:\n if i not in files:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n else:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n\nmsg['payload'] = True\n\nreturn msg", "outputs": 1, "x": 520, "y": 800, "wires": [ [ "960912e90ba5b5bc", "ea0e57d83f291e23" ] ] }, { "id": "38783aea9cc317a6", "type": "link in", "z": "829d803b6033a693", "name": "factory reset", "links": [ "80bccc884b0be297", "beacc3dc5398fa79" ], "x": 135, "y": 840, "wires": [ [ "544d20f02215011a" ] ] }, { "id": "f20da2fc4978b7bf", "type": "link out", "z": "829d803b6033a693", "name": "global", "mode": "link", "links": [ "d14bbbb446d45e39" ], "x": 345, "y": 400, "wires": [] }, { "id": "8e39e4a037487ecd", "type": "python3-function", "z": "829d803b6033a693", "name": "create log", "func": "import subprocess\nfrom time import sleep\nsleep(20)\n\n\nlog = '############################################DMESG############################################\\n'\nlog += subprocess.getoutput(\"dmesg\")\nlog += '\\n############################################SYSLOG############################################\\n'\nlog += subprocess.getoutput(\"tail -10000 /var/log/syslog\")\n\nwith open('/home/pi/OpenScan/tmp/log.txt', 'w+') as file:\n file.write(log)\n\nreturn msg", "outputs": 1, "x": 240, "y": 560, "wires": [ [] ] }, { "id": "be8cae9cf6f3585f", "type": "ui_template", "z": "829d803b6033a693", "group": "1f7f7e1e24f5ad9b", "name": "first start", "order": 1, "width": 6, "height": 3, "format": "

Initial Setup

\n

Note, that you can always adjust these and other settings in the settings menu, which will appear after this setup stage. 

\n

Model

\n

Please select the OpenScan Version - this will only affect the motor settings (acceleration, gear ratio, speed).

\n

Camera

\n

- Pi Camera v1, v2, HQ, Arducam IMX519, IMX290, IMX378, OV9281 are connected through the ribbon cable. If you encounter any issues, please check the cable's orientation

\n

- DSLR (gphoto) - can be used with a wide range of cameras, which can be connected and controlled via USB. Check GPhoto if your camera is supported

\n

- External Camera - Can be used to connect your camera trigger to the GPIO pins on the front of the pi shield. This can be used with any (modified) remote shutter release, and thus it is possible to use Smartphones, DSLR and compact cameras

", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 280, "y": 40, "wires": [ [] ] }, { "id": "8955d11554f55e63", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "5b3e5aca21140e9a", "order": 1, "width": 6, "height": 3, "passthru": false, "label": "Install Updates", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "date", "topic": "", "topicType": "str", "x": 120, "y": 280, "wires": [ [ "1e7457ea9c2c5e09" ] ] }, { "id": "1e7457ea9c2c5e09", "type": "link out", "z": "829d803b6033a693", "name": "update", "mode": "link", "links": [ "39a502b38837273d" ], "x": 245, "y": 280, "wires": [] }, { "id": "bb84b9e5c7d8e21f", "type": "python3-function", "z": "829d803b6033a693", "name": "rescue incomplete project", "func": "#if project has not been done properly, this is a way to rescue the file\n\nfrom os import system\nfrom os.path import isfile\nfrom time import strftime\nfrom OpenScan import load_str\n\nbasepath = '/home/pi/OpenScan/'\nzippath = basepath + 'tmp/tmp.zip'\nprojectname=load_str(\"routine_projectname\")\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\n\nsystem('chmod -R 777 /home/pi/OpenScan/')\n\nif isfile(zippath):\n system('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')", "outputs": 1, "x": 290, "y": 600, "wires": [ [] ] }, { "id": "a291fc98e4269c1b", "type": "ui_text", "z": "829d803b6033a693", "group": "729f9ea6e3513c9b", "order": 7, "width": 4, "height": 1, "name": "version", "label": "Version:", "format": "{{msg.firmware}}", "layout": "row-center", "className": "", "x": 460, "y": 360, "wires": [] }, { "id": "7113d7b25a851151", "type": "function", "z": "829d803b6033a693", "name": "FIRMWARE VERSION", "func": "msg.firmware = '2024-09-09'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 360, "wires": [ [ "a291fc98e4269c1b", "ec5cefa70ff535f7" ] ] }, { "id": "ec5cefa70ff535f7", "type": "ui_text", "z": "829d803b6033a693", "group": "ddbd496e.93a288", "order": 2, "width": 6, "height": 1, "name": "current version", "label": "Current version:", "format": "{{msg.firmware}}", "layout": "row-spread", "className": "", "x": 480, "y": 320, "wires": [] }, { "id": "c4c1580c289fc7bd", "type": "python3-function", "z": "829d803b6033a693", "name": "create path", "func": "import os\n\npaths = ['/home/pi/OpenScan/scans/preview/']\n\n\nfor i in paths:\n if not os.path.isdir(i):\n os.mkdir(i)", "outputs": 1, "x": 250, "y": 640, "wires": [ [] ] }, { "id": "06d33bb8951ce668", "type": "ui_template", "z": "829d803b6033a693", "group": "", "name": "donate", "order": 2, "width": "0", "height": "0", "format": "\n\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "global", "className": "", "x": 450, "y": 40, "wires": [ [] ] }, { "id": "245e4341d4fb611c", "type": "function", "z": "829d803b6033a693", "name": "pinmap_v2", "func": "msg = { \n'overwrite':true,\n'settings':{\n 'pin_rotor_endstop':27,\n 'pin_tt_endstop':5,\n 'pin_extra_endstop':26,\n 'pin_external':25,\n 'pin_ringlight1':24,\n 'pin_ringlight2':24,\n 'pin_rotor_dir':23,\n 'pin_rotor_enable':19,\n 'pin_rotor_step':22,\n 'pin_tt_dir':6,\n 'pin_tt_enable':19,\n 'pin_tt_step':16,\n 'pin_extra_dir':21,\n 'pin_extra_step':20,\n 'pin_extra_enable':19,\n 'extra_acc':1,\n 'extra_accramp':200,\n 'extra_angle':10,\n 'extra_delay':0.0001,\n 'extra_dir':1,\n 'extra_stepsperrotation':3200,\n}}\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 870, "y": 40, "wires": [ [ "627406f3611511dc" ] ] }, { "id": "627406f3611511dc", "type": "python3-function", "z": "829d803b6033a693", "name": "write", "func": "from os import listdir\n\nbasedir = '/home/pi/OpenScan/settings/'\n\nmsg['payload'] = ''\n\nfiles = listdir(basedir)\n\nfor i in msg['settings']:\n if msg['overwrite'] != True:\n if i not in files:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n else:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n\nmsg['payload'] = True\n\nreturn msg", "outputs": 1, "x": 1010, "y": 40, "wires": [ [ "50eeb3e362f9027f" ] ] }, { "id": "88b1bddde110298a", "type": "inject", "z": "829d803b6033a693", "name": "", "props": [ { "p": "overwrite", "v": "false", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": "0.1", "topic": "", "x": 730, "y": 40, "wires": [ [ "245e4341d4fb611c" ] ] }, { "id": "50eeb3e362f9027f", "type": "link out", "z": "829d803b6033a693", "name": "started1s", "mode": "link", "links": [ "2f4c0f98.dee2", "397ab7f44b893c89", "65145c939b6647e2", "65b38bfeb3fee710", "6d1e12f51f9af0b6", "788fabff98c7973c", "9b2bc9849aee310b", "a1e14624058e74cd", "a67c18aaca2f5fa5", "bd80ec228fb9a86d", "cc9c4092edeb43cc", "d3fc91d87d5d5f62", "d7c1fb4c028b21a5", "e5f38b4a07a5e278", "f0b355967b33dfee", "d0104e0163745993", "5e7d5e4335d37794" ], "x": 1095, "y": 40, "wires": [] }, { "id": "ea0e57d83f291e23", "type": "debug", "z": "829d803b6033a693", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 610, "y": 860, "wires": [] }, { "id": "4f3121f158f06a61", "type": "python3-function", "z": "829d803b6033a693", "name": "Rotor left", "func": "from OpenScan import motorrun, load_int\nfrom time import sleep\n\nmotorrun('rotor',100,True)\n\nmotorrun('tt',360,True)\nmotorrun('extra',360,True)", "outputs": 1, "x": 940, "y": 80, "wires": [ [] ] }, { "id": "4a8a04b1e5dca8fe", "type": "inject", "z": "829d803b6033a693", "name": "run rotor till endstop", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 770, "y": 80, "wires": [ [ "4f3121f158f06a61" ] ] }, { "id": "7494af94bc76a6a8", "type": "python3-function", "z": "829d803b6033a693", "name": "fix_endstops", "func": "from OpenScan import load_int, save\n\nif load_int('pin_rotor_endstop') == 27 and load_int('pin_tt_endstop') == 5:\n save('pin_rotor_endstop',24)\n save('pin_tt_endstop',25)", "outputs": 1, "x": 250, "y": 680, "wires": [ [] ] }, { "id": "828e5298.d2192", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 9, "width": 2, "height": 1, "passthru": false, "label": "⇐", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 400, "wires": [ [ "b12e54fb.3141b8" ] ] }, { "id": "96c7e241.458e6", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 10, "width": 2, "height": 1, "passthru": false, "label": "⇒", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 440, "wires": [ [ "37f52dd4.bd7572" ] ] }, { "id": "2e854876.6b6008", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 6, "width": 2, "height": 1, "passthru": true, "label": "⇑", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 280, "wires": [ [ "555aea34.b3b5e4" ] ] }, { "id": "753817f.1b9b3e8", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 7, "width": 2, "height": 1, "passthru": true, "label": "⇓", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 320, "wires": [ [ "9905e0c9.dddcd" ] ] }, { "id": "8775044.3aa3ef8", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 8, "width": 2, "height": 1, "name": "", "label": "Turntable", "format": "", "layout": "row-left", "className": "", "x": 100, "y": 360, "wires": [] }, { "id": "9e8a2d23.bf6ce", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 5, "width": 2, "height": 1, "name": "", "label": "Rotor", "format": "", "layout": "row-left", "className": "", "x": 90, "y": 240, "wires": [] }, { "id": "555aea34.b3b5e4", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 280, "wires": [ [ "46e00b45.c24ca4" ] ] }, { "id": "9905e0c9.dddcd", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 320, "wires": [ [ "6ee089cb343a35ef" ] ] }, { "id": "b12e54fb.3141b8", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 400, "wires": [ [ "c1871a2b9af5419a" ] ] }, { "id": "37f52dd4.bd7572", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 440, "wires": [ [ "42b9f1fc49e69f54" ] ] }, { "id": "46e00b45.c24ca4", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Rotor left", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('rotor',load_int('rotor_angle'))", "outputs": 1, "x": 360, "y": 280, "wires": [ [] ] }, { "id": "6ee089cb343a35ef", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Rotor right", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('rotor',-load_int('rotor_angle'))", "outputs": 1, "x": 370, "y": 320, "wires": [ [] ] }, { "id": "42b9f1fc49e69f54", "type": "python3-function", "z": "1613373abaf77a2c", "name": "TT right", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('tt',-load_int('tt_angle'))", "outputs": 1, "x": 360, "y": 440, "wires": [ [] ] }, { "id": "c1871a2b9af5419a", "type": "python3-function", "z": "1613373abaf77a2c", "name": "TT left", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('tt',load_int('tt_angle'))", "outputs": 1, "x": 350, "y": 400, "wires": [ [] ] }, { "id": "aebad788761dce4a", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "routine_photocount", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 14, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "10", "max": "300", "step": "10", "className": "", "x": 350, "y": 540, "wires": [ [ "ce28a0b5bfb0d5a1" ] ] }, { "id": "107a030938cbfea9", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 540, "wires": [ [ "aebad788761dce4a" ] ] }, { "id": "ce28a0b5bfb0d5a1", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 540, "wires": [ [] ] }, { "id": "84d6b96c8ebaac96", "type": "function", "z": "1613373abaf77a2c", "name": "loadF", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) / 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 580, "wires": [ [ "470b10726d298834" ] ] }, { "id": "470b10726d298834", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "shutter ", "label": " ", "tooltip": "", "group": "7aaf184330605300", "order": 16, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "700", "step": "1", "className": "", "x": 310, "y": 580, "wires": [ [ "44c3947a9b92d32d" ] ] }, { "id": "44c3947a9b92d32d", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload * 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 580, "wires": [ [] ] }, { "id": "069bcf58b1fe44cd", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 13, "width": 3, "height": 1, "name": "photocount", "label": "Photos", "format": "", "layout": "row-left", "className": "", "x": 670, "y": 540, "wires": [] }, { "id": "8dc7df1de59cb03a", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 15, "width": 3, "height": 1, "name": "shutter", "label": "Shutter (ms)", "format": "", "layout": "row-left", "className": "", "x": 650, "y": 580, "wires": [] }, { "id": "cc69dba8d54a29dd", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "Crop X", "label": " ", "tooltip": "", "group": "7aaf184330605300", "order": 18, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "99", "step": "1", "className": "", "x": 320, "y": 620, "wires": [ [ "c2b2ab5524271123" ] ] }, { "id": "e3a90602605fb9e9", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "Crop Y", "label": " ", "tooltip": "", "group": "7aaf184330605300", "order": 20, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "99", "step": "1", "className": "", "x": 310, "y": 660, "wires": [ [ "26f17a7f406df73c" ] ] }, { "id": "9c6b48b7b4cc4e1a", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 620, "wires": [ [ "cc69dba8d54a29dd" ] ] }, { "id": "c470fd0b15356206", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 660, "wires": [ [ "e3a90602605fb9e9" ] ] }, { "id": "c2b2ab5524271123", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 620, "wires": [ [] ] }, { "id": "26f17a7f406df73c", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 660, "wires": [ [] ] }, { "id": "fecf5cff888bb570", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 17, "width": 3, "height": 1, "name": "cropx", "label": "{{msg.crop1}}", "format": "", "layout": "row-left", "className": "", "x": 690, "y": 620, "wires": [] }, { "id": "0ee4950bd21498bd", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 19, "width": 3, "height": 1, "name": "cropy", "label": "{{msg.crop2}}", "format": "", "layout": "row-left", "className": "", "x": 690, "y": 660, "wires": [] }, { "id": "ebbf11b55d758806", "type": "ui_text_input", "z": "1613373abaf77a2c", "name": "", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 4, "width": 3, "height": 1, "passthru": true, "mode": "text", "delay": "0", "topic": "", "sendOnBlur": true, "className": "", "topicType": "str", "x": 320, "y": 500, "wires": [ [ "67385b196c517ac6" ] ] }, { "id": "f4b3112a9ec6c487", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.payload=\"default\"\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 500, "wires": [ [ "ebbf11b55d758806" ] ] }, { "id": "67385b196c517ac6", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'routine_projectname'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload).replace(/ /g, '_')\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 500, "wires": [ [] ] }, { "id": "4dd7285c2b0fd79b", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "ringlight", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 12, "width": 3, "height": 1, "passthru": true, "outs": "all", "topic": "", "topicType": "str", "min": 0, "max": "3", "step": 1, "className": "", "x": 320, "y": 700, "wires": [ [ "873dace18a23fdf2" ] ] }, { "id": "873dace18a23fdf2", "type": "python3-function", "z": "1613373abaf77a2c", "name": "LED", "func": "from OpenScan import ringlight\nval = msg['payload']\n\nif val == 0:\n ringlight(1,False)\n ringlight(2,False)\nelif val == 1:\n ringlight(1,False)\n ringlight(2,True)\nelif val == 2:\n ringlight(1,True)\n ringlight(2,False)\nelif val == 3:\n ringlight(1,True)\n ringlight(2,True)", "outputs": 1, "x": 510, "y": 700, "wires": [ [] ] }, { "id": "9e30e33a1520fee0", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "msg.payload = 0\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 700, "wires": [ [ "4dd7285c2b0fd79b" ] ] }, { "id": "7dd287f40385922f", "type": "ui_button", "z": "1613373abaf77a2c", "name": "start ", "group": "7aaf184330605300", "order": 21, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-play", "payload": "", "payloadType": "date", "topic": "enabled", "topicType": "str", "x": 150, "y": 880, "wires": [ [ "431f917c2541ae48", "33d94a04b96a2de0", "6d15f717d5a11002" ] ] }, { "id": "579f2211199fd6ab", "type": "ui_button", "z": "1613373abaf77a2c", "name": "stop", "group": "7aaf184330605300", "order": 23, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-stop", "payload": "numberofphotos", "payloadType": "global", "topic": "", "topicType": "str", "x": 810, "y": 960, "wires": [ [ "1787f08ed7070ddd", "c1c044f3c2139f68" ] ] }, { "id": "431f917c2541ae48", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Routine", "func": "from OpenScan import load_bool, load_str, load_int, load_float, motorrun, create_coordinates, take_photo, save, load_bool, camera\nfrom time import sleep, strftime, time\nfrom zipfile import ZipFile, ZIP_DEFLATED\nfrom os import system\nfrom os.path import isfile\nfrom Arducam import Focuser\n\nif load_str(\"status_internal_cam\")==\"no camera found\" or load_str(\"status_internal_cam\")[:5]==\"Featu\":\n return\n\nsave('status_internal_cam','Routine-preparing')\n\nprojectname=load_str(\"routine_projectname\")\nphotocount = load_int('routine_photocount') #vorher point_count\nangle_max = load_int('rotor_anglemax')\nangle_min = load_int('rotor_anglemin')\nangle_start = load_int('rotor_anglestart')\ncam = load_str('camera')\nSTmode = load_bool('cam_STmode')\ntt_mode = load_bool('turntable_mode')\ncam_delay_after = load_float('cam_delay_after')\ncam_delay_before = load_float('cam_delay_before')\n\nif cam == 'imx519' and STmode == True:\n focuser = Focuser('/dev/v4l-subdev1')\n stacksize = load_int('cam_stacksize')\n focus1 = load_int('cam_focus1')\n focus2 = load_int('cam_focus2')\n if focus1 > focus2:\n focus2 = focus1\n focus1 = load_int('cam_focus2') \n focusstep = int((focus2-focus1)/(stacksize - 1))\n\ncounter = 0\nbasepath = '/home/pi/OpenScan/'\ntemppath = basepath + 'tmp/tmp.jpg'\nzippath = basepath + 'tmp/tmp.zip'\n\nif not 'projectcode' in msg:\n projectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\n msg['projectcode'] = projectcode\n msg['counter'] = -1\n if isfile(zippath):\n system('rm ' + zippath)\n sleep(1)\n\nprojectcode = msg['projectcode']\nmsg['counter'] += 1\n\nif tt_mode == False:\n coordinates = create_coordinates(angle_min,angle_max,photocount)\nelse:\n angle_start = 0\n coordinates = []\n for i in range (photocount):\n coordinates.append([0,360/photocount*(i+1)])\n\nposition_last = (angle_start , 0)\n\nzip = ZipFile(zippath, \"a\",ZIP_DEFLATED, allowZip64=True)\n\nstarttime = time()\n\nfor position in coordinates:\n counter += 1\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n\n while load_str('status_internal_cam') == 'Routine-paused':\n sleep(0.2)\n\n filepath = basepath + 'tmp/' + projectname + '_' + str(counter) + \".jpg\"\n\n rotor_angle = position_last[0] - position[0]\n if abs(rotor_angle) > 180:\n rotor_angle = -360 * rotor_angle/abs(rotor_angle) + rotor_angle\n\n tt_angle = position_last[1] - position[1]\n if tt_angle > 180:\n tt_angle -= 360\n elif tt_angle < -180:\n tt_angle += 360\n # tt_angle = -360 * tt_angle/abs(tt_angle) + tt_angle\n \n motorrun('rotor', rotor_angle)\n motorrun('tt', tt_angle)\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n while load_str('status_internal_cam') == 'Routine-paused':\n sleep(0.2)\n\n msg['cropx'] = load_int('cam_cropx')\n msg['cropy'] = load_int('cam_cropy')\n msg['rotation'] = load_int('cam_rotation')\n msg['filepath_in'] = 'tmp/tmp.jpg'\n msg['filepath_out'] = 'tmp/tmp.jpg'\n msg['filepath'] = 'tmp/tmp.jpg'\n\n if counter < 6:\n ETA = ''\n sleep(cam_delay_before)\n if STmode == True:\n counter2 = 0\n for focus in range (stacksize):\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n while load_str('status_internal_cam') == 'Routine-paused':\n sleep(0.2)\n counter2 += 1\n save('status_internal_cam','Routine-' + str(counter) + '/' + str(photocount) + ' F' + str(counter2) + ETA)\n focuser.write(focus1 + focus * focusstep)\n take_photo('tmp/tmp.jpg')\n camera('/crop',msg)\n zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + '-' + str(focus) + \".jpg\")\n system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\n elif cam != 'external':\n save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\n\n if cam == 'gphoto':\n camera('/gphoto_capture', msg)\n if cam in ('usb_webcam','imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\n take_photo('tmp/tmp.jpg')\n camera('/crop',msg)\n \n zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + \".jpg\")\n system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\n elif cam == 'external':\n camera('external_capture')\n save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\n\n ETA = '-ETA:'+str(int((photocount/counter - 1)*(time() - starttime)))+'/'+str(int(photocount/counter*(time() - starttime)))+'s'\n sleep(cam_delay_after)\n\n position_last = position\n\nzip.close()\n\nsave('status_internal_cam','Routine-done')\n\nmotorrun('rotor',position_last[0] - angle_start)\nmotorrun('tt',position_last[1])\n\nsave('status_internal_cam','--READY--')\n\nif load_bool('routine_secondpass')==True:\n msg['topic'] = 'Scan done'\n msg['payload'] = 'Do you want to run another pass or finish this project?'\n msg['enabled'] = False\n return msg,None\n\nreturn None,msg\n", "outputs": 2, "x": 300, "y": 880, "wires": [ [ "db7eea74d3bf892b" ], [ "0b8661103366f834" ] ] }, { "id": "1787f08ed7070ddd", "type": "python3-function", "z": "1613373abaf77a2c", "name": "stop", "func": "from OpenScan import load_str, save\n\nstatus = load_str('status_internal_cam')\n\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\n return\n\nsave('status_internal_cam', 'Routine-stopping')", "outputs": 1, "x": 930, "y": 960, "wires": [ [] ] }, { "id": "e9b13dfd9f8d3711", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6" ], "x": 395, "y": 840, "wires": [] }, { "id": "9654deebb668e012", "type": "inject", "z": "1613373abaf77a2c", "name": "1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "", "payloadType": "date", "x": 290, "y": 1000, "wires": [ [ "c1c044f3c2139f68" ] ] }, { "id": "8367cfa0bf5bc5df", "type": "link in", "z": "1613373abaf77a2c", "name": "start routine", "links": [ "210ef5246d1a8790", "84608db962fd9932", "8689e938.dd9e38", "f20f2dbc.0f123", "e9b13dfd9f8d3711", "96bdb9417e38810f", "fb13752beddee9f2", "bd75f33b8a57c522" ], "x": 55, "y": 880, "wires": [ [ "7dd287f40385922f" ] ] }, { "id": "fb13752beddee9f2", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "2f4c0f98.dee2", "8367cfa0bf5bc5df", "b33d604c.5f1a6" ], "x": 895, "y": 920, "wires": [] }, { "id": "95439678bb2df2a2", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "msg.flag = global.get('flag')\nif (global.get('flag_pw')== true){\n return msg\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 1220, "wires": [ [ "04cc2467807d2d6b", "14f9617b5b301318" ] ] }, { "id": "948a3ae4444685f2", "type": "change", "z": "1613373abaf77a2c", "name": "flag_pw true", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 610, "y": 1260, "wires": [ [] ] }, { "id": "04cc2467807d2d6b", "type": "change", "z": "1613373abaf77a2c", "name": "flag_pw false", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 390, "y": 1260, "wires": [ [] ] }, { "id": "12f1399b240830bf", "type": "exec", "z": "1613373abaf77a2c", "command": " v4l2-ctl --list-formats-ext", "addpay": "", "append": "", "useSpawn": "true", "timer": "", "winHide": false, "oldrc": false, "name": "check cam", "x": 190, "y": 100, "wires": [ [ "6222f781629c72e7" ], [ "6222f781629c72e7" ], [] ] }, { "id": "6222f781629c72e7", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\ncontent = '--READY--'\n\nif (msg.payload.includes('Cannot open device')){\n content = 'no camera found'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return msg\n }\n });\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 350, "y": 100, "wires": [ [ "e89c16809f8a5f1c" ] ] }, { "id": "e978bf8c53d1f15a", "type": "comment", "z": "1613373abaf77a2c", "name": "Settings internal cam", "info": "", "x": 120, "y": 40, "wires": [] }, { "id": "ccb7da246de908d1", "type": "comment", "z": "1613373abaf77a2c", "name": "preview internal cam", "info": "", "x": 110, "y": 1160, "wires": [] }, { "id": "e9566588c5e40637", "type": "inject", "z": "1613373abaf77a2c", "name": "4s/0.5", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.5", "crontab": "", "once": true, "onceDelay": "4", "topic": "Repeat", "payload": "0.2", "payloadType": "str", "x": 80, "y": 1220, "wires": [ [ "95439678bb2df2a2" ] ] }, { "id": "14f9617b5b301318", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Take Preview Shot", "func": "from time import time, sleep\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\nsleep(0.5)\n\n\nstatus = load_str('status_internal_cam')\ncam=load_str('camera')\n\n\nif msg['flag'] == False and not 'Routine' in status:\n return msg\n\nif cam == 'external':\n return\n\nmsg['payload']=\"/tmp/preview.jpg?ts=\"+str(int(time()*10))\n\nif cam == 'gphoto' and status == 'no camera found':\n if camera('/gphoto_init') == 200:\n save('status_internal_cam','--READY--')\n\nif status!=\"--READY--\":\n return msg\n\nmsg['cropx'] = load_int('cam_cropx')\nmsg['cropy'] = load_int('cam_cropy')\nmsg['rotation'] = load_int('cam_rotation')\nmsg['filepath_in'] = 'tmp/tmp.jpg'\nmsg['filepath_out'] = 'tmp/preview.jpg'\nmsg['filepath'] = 'tmp/tmp.jpg'\nmsg['preview'] = True\n\nif cam == 'gphoto':\n if camera('/gphoto_test', msg) != 200:\n save('status_internal_cam','no camera found')\n return msg\n camera('/gphoto_preview', msg)\n\nif cam in ('usb_webcam', 'imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\n take_photo('tmp/tmp.jpg')\n\ncamera('/crop',msg)\n\nreturn msg\n", "outputs": 1, "x": 410, "y": 1220, "wires": [ [ "948a3ae4444685f2", "991b587d406d0d91", "8f5d87ce24c40b11" ] ] }, { "id": "991b587d406d0d91", "type": "ui_template", "z": "1613373abaf77a2c", "group": "ce9cc9d915dc6eb6", "name": "preview_internal", "order": 1, "width": 12, "height": 12, "format": "
\n\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 620, "y": 1220, "wires": [ [] ] }, { "id": "1118d0965ff7c40b", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 3, "width": 3, "height": 1, "name": "projectname", "label": "Projectname", "format": "", "layout": "row-left", "className": "", "x": 670, "y": 500, "wires": [] }, { "id": "82c8ad50ecfbc755", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 11, "width": 3, "height": 1, "name": "ringlight", "label": "Ringlight", "format": "", "layout": "row-left", "className": "", "x": 660, "y": 700, "wires": [] }, { "id": "33d94a04b96a2de0", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "global.set('flag', false)\n\nvar file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\n\n\nif (data === 'no camera found' || data.substring(0,5) === 'Featu'){\n return\n}\n\nmsg.enabled = true\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 960, "wires": [ [ "579f2211199fd6ab", "c433515042ba01b5" ] ] }, { "id": "c1c044f3c2139f68", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 1000, "wires": [ [ "579f2211199fd6ab", "c433515042ba01b5" ] ] }, { "id": "9a368472a72fbc48", "type": "comment", "z": "1613373abaf77a2c", "name": "preview arducam with focus", "info": "", "x": 140, "y": 1360, "wires": [] }, { "id": "8f5d87ce24c40b11", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "preview_arducam", "order": 2, "width": 10, "height": 12, "format": "
\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 630, "y": 1300, "wires": [ [] ] }, { "id": "282efe64332193c8", "type": "python3-function", "z": "1613373abaf77a2c", "name": "focus", "func": "from OpenScan import load_str\n\nif load_str('camera') != 'imx519':\n return\n\nfrom Arducam import Focuser\n\n\nif msg['focuser'] == True:\n focuser = Focuser('/dev/v4l-subdev1')\n focuser.write(msg['focus'])\n return msg", "outputs": 1, "x": 1110, "y": 1460, "wires": [ [] ] }, { "id": "64b16ef47ab6d859", "type": "ui_switch", "z": "1613373abaf77a2c", "name": "MF", "label": "", "tooltip": "", "group": "90223f7ddc082321", "order": 4, "width": 1, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "false", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "true", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 150, "y": 1400, "wires": [ [ "f017f67a8d4a3750" ] ] }, { "id": "f017f67a8d4a3750", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "let fs = global.get('fs');\nfilepath = '/home/pi/OpenScan/settings/';\n\nvar file = 'status_internal_cam'\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data != '--READY--'){\n return\n}\n\nfile = 'cam_AFmode'\ncontent = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n});\n\nglobal.set('AF',msg.payload)\nmsg.enabled = false\nif (msg.payload == false){\n msg.enabled = true\n}\nif (msg.payload == true){\n file = 'cam_focus1'\n content = String(0)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n file = 'cam_focus2'\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \n file = 'cam_stacksize'\n content = String(2)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n global.set('focus1', 0)\n global.set('focus2', 0)\n\n}\n\n\nmsg.focus = global.get('focus')\nmsg.payload = 'down'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 1400, "wires": [ [ "5c39bd09.702d84", "74521cf72050b515", "b70e8c24ee011258", "a2ff9dfd858821bc", "ef62086d10d830fd" ] ] }, { "id": "65145c939b6647e2", "type": "link in", "z": "1613373abaf77a2c", "name": "", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 55, "y": 1400, "wires": [ [ "64b16ef47ab6d859" ] ] }, { "id": "5ea18678.975138", "type": "trigger", "z": "1613373abaf77a2c", "name": "20ms", "op1": "", "op2": "0", "op1type": "pay", "op2type": "str", "duration": "-20", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 1440, "wires": [ [ "fd93843e238cc9ce" ] ] }, { "id": "5c39bd09.702d84", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "F+", "order": 8, "width": 1, "height": 1, "format": " ", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 410, "y": 1400, "wires": [ [ "dcfb5cce.0431a" ] ] }, { "id": "dcfb5cce.0431a", "type": "switch", "z": "1613373abaf77a2c", "name": "", "property": "payload", "propertyType": "msg", "rules": [ { "t": "eq", "v": "1", "vt": "num" }, { "t": "eq", "v": "-1", "vt": "num" }, { "t": "eq", "v": "up", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 3, "x": 550, "y": 1420, "wires": [ [ "5ea18678.975138", "f4a41b1e7b221486" ], [ "5ea18678.975138", "f4a41b1e7b221486" ], [ "8cdd0a6b.40bcd8" ] ] }, { "id": "8cdd0a6b.40bcd8", "type": "change", "z": "1613373abaf77a2c", "name": "", "rules": [ { "t": "set", "p": "reset", "pt": "msg", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 560, "y": 1480, "wires": [ [ "5ea18678.975138", "e9b3837b1ffb0360" ] ] }, { "id": "74521cf72050b515", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "F-", "order": 9, "width": 1, "height": 1, "format": " ", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 410, "y": 1440, "wires": [ [ "dcfb5cce.0431a" ] ] }, { "id": "7219f62c9fdc6753", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 7, "width": 2, "height": 1, "name": "", "label": "{{msg.payload}}", "format": "", "layout": "col-center", "className": "", "x": 1130, "y": 1420, "wires": [] }, { "id": "b70e8c24ee011258", "type": "function", "z": "1613373abaf77a2c", "name": "global", "func": "if (msg.payload == 'down'){\n msg.enabled = false\n msg.payload = ' '\n msg.focuser = global.get('focuser')\n return msg\n}\n\n\nmsg.enabled = true\n\nsign = msg.payload\nfocus = global.get('focus')\nif (focus > 3000){\n focusstep = 5\n}\nelse if (focus <=3000 && focus > 2000){\n focusstep = 3\n}\nelse{\n focusstep = 2\n}\n\n\nfocus = focus + sign * focusstep\n\nsign = msg.payload\nif (focus > 4000){\n distance = 6\n focus = 4000\n}\nelse if (focus > 1200 && focus <= 4000){\n distance = 737086 * Math.pow(focus, -1.4096)\n}\nelse if (focus <= 1200){\n distance = 999\n if (focus <=0){\n focus = 0\n }\n}\n\n\nglobal.set('focus', focus)\nmsg.focus = focus\nmsg.distance = distance\ndistance = distance * 10\nmsg.focuser = global.get('focuser')\nmsg.payload = String(distance.toFixed(1)) + 'mm'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 970, "y": 1440, "wires": [ [ "7219f62c9fdc6753", "282efe64332193c8", "704a9f89089d1f25" ] ] }, { "id": "f4a41b1e7b221486", "type": "change", "z": "1613373abaf77a2c", "name": "focuser f", "rules": [ { "t": "set", "p": "focuser", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 740, "y": 1400, "wires": [ [] ] }, { "id": "e9b3837b1ffb0360", "type": "change", "z": "1613373abaf77a2c", "name": "focuser t", "rules": [ { "t": "set", "p": "focuser", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 740, "y": 1480, "wires": [ [] ] }, { "id": "fd93843e238cc9ce", "type": "delay", "z": "1613373abaf77a2c", "name": "10ms", "pauseType": "delay", "timeout": "20", "timeoutUnits": "milliseconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 850, "y": 1440, "wires": [ [ "b70e8c24ee011258" ] ] }, { "id": "25c4138bddb77b6b", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "set", "order": 10, "width": 2, "height": 1, "format": "set", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 570, "y": 1540, "wires": [ [ "95e1d239988b29e0" ] ] }, { "id": "95e1d239988b29e0", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "focus = global.get('focus')\nfocus1 = global.get('focus1')\nfocus2 = global.get('focus2')\nlet fs = global.get('fs');\nfilepath = '/home/pi/OpenScan/settings/';\n \nif (msg.payload == false){\n return msg\n}\n\nif (focus1 != 0 && focus2 != 0){\n global.set('focus1', 0)\n global.set('focus2', 0)\n file = 'cam_focus1'\n content = String(0)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n file = 'cam_focus2'\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n msg.distance1 = ' '\n msg.distance2 = ' '\n msg.enabled = false\n return msg\n}\n\nif (focus > 4000){\n distance = 6\n focus = 4000\n}\nelse if (focus > 1200 && focus <= 4000){\n distance = 737086 * Math.pow(focus, -1.4096)\n}\nelse if (focus <= 1200){\n distance = 999.9\n if (focus <=0){\n focus = 0\n }\n}\ndistance = distance * 10\n\nif (focus1 == 0){\n global.set('focus1', focus)\n file = 'cam_focus1'\n content = String(focus)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n global.set('distance1', distance)\n msg.distance1 = distance.toFixed(1)\n msg.distance2 = 'tbd'\n msg.enabled = false\n return msg\n}\nif (focus1 != 0 && focus2 ==0 && focus!= focus1){\n global.set('focus2', focus)\n file = 'cam_focus2'\n content = String(focus)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n global.set('distance2', distance)\n msg.distance1 = global.get('distance1').toFixed(1)\n msg.distance2 = distance.toFixed(1)\n msg.enabled = true\n return msg\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 710, "y": 1560, "wires": [ [ "7889245e91ddea4b", "210ef5246d1a8790" ] ] }, { "id": "7889245e91ddea4b", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 11, "width": 2, "height": 1, "name": "", "label": "{{msg.distance1}}", "format": "{{msg.distance2}}", "layout": "col-center", "className": "", "x": 830, "y": 1600, "wires": [] }, { "id": "a1d29e56599da0bd", "type": "link in", "z": "1613373abaf77a2c", "name": "focusnumber", "links": [ "210ef5246d1a8790", "2dd2503d7ab0214b", "6b94bf2295b1b31d" ], "x": 175, "y": 1760, "wires": [ [ "06504f47ee1744d7", "5f8b90ef08a7d68c" ] ] }, { "id": "210ef5246d1a8790", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "a1d29e56599da0bd", "8367cfa0bf5bc5df", "149e2e46b9623a2d" ], "x": 835, "y": 1560, "wires": [] }, { "id": "b6f37e23f2491639", "type": "ui_switch", "z": "1613373abaf77a2c", "name": "Stack", "label": "", "tooltip": "", "group": "90223f7ddc082321", "order": 6, "width": 1, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 290, "y": 1600, "wires": [ [ "2d66216fee29250c" ] ] }, { "id": "a2ff9dfd858821bc", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "msg.payload = false\nif (msg.enabled == false){\n return msg\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 1560, "wires": [ [ "25c4138bddb77b6b", "7889245e91ddea4b", "4cfada2de1c5bb74", "95e1d239988b29e0" ] ] }, { "id": "2d66216fee29250c", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "file = 'cam_STmode'\nlet fs = global.get('fs');\nfilepath = '/home/pi/OpenScan/settings/';\ncontent = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n});\n\nmsg.enabled = true\nglobal.set('ST',msg.payload)\nif (msg.payload == false){\n global.set('focus1',0)\n global.set('focus2',0)\n file = 'cam_focus1'\n content = String(0)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n file = 'cam_focus2'\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \n \n msg.enabled = false\n}\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 1600, "wires": [ [ "25c4138bddb77b6b", "7889245e91ddea4b", "2dd2503d7ab0214b", "4cfada2de1c5bb74" ] ] }, { "id": "ef62086d10d830fd", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "msg.payload = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 150, "y": 1560, "wires": [ [ "b6f37e23f2491639", "523019d0a2c698f5" ] ] }, { "id": "06504f47ee1744d7", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 12, "width": 2, "height": 1, "name": "", "label": "Stacksize:", "format": "{{msg.stacksize}}", "layout": "row-center", "className": "", "x": 710, "y": 1760, "wires": [] }, { "id": "2dd2503d7ab0214b", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "a1d29e56599da0bd" ], "x": 535, "y": 1620, "wires": [] }, { "id": "21306d6402225553", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.stacksize = msg.payload\nmsg.enabled = true\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 550, "y": 1720, "wires": [ [ "06504f47ee1744d7", "ca184d58f7deb4b1", "84608db962fd9932" ] ] }, { "id": "e2f8fdd47bdd1b66", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "stacksize", "label": " ", "tooltip": "", "group": "90223f7ddc082321", "order": 13, "width": 2, "height": 1, "passthru": true, "outs": "end", "topic": "", "topicType": "str", "min": "2", "max": "20", "step": "1", "className": "", "x": 400, "y": 1720, "wires": [ [ "21306d6402225553" ] ] }, { "id": "523019d0a2c698f5", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 5, "width": 1, "height": 1, "name": "", "label": "St", "format": "", "layout": "col-center", "className": "", "x": 290, "y": 1560, "wires": [] }, { "id": "dfbfe28bac5c4221", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 3, "width": 1, "height": 1, "name": "MF", "label": "MF", "format": "", "layout": "col-center", "className": "", "x": 150, "y": 1440, "wires": [] }, { "id": "ca184d58f7deb4b1", "type": "function", "z": "1613373abaf77a2c", "name": "save", "func": "var file = 'cam_stacksize'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.stacksize)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 690, "y": 1720, "wires": [ [] ] }, { "id": "704a9f89089d1f25", "type": "function", "z": "1613373abaf77a2c", "name": "save", "func": "var file = 'cam_focus'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.focus)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1110, "y": 1500, "wires": [ [] ] }, { "id": "5f8b90ef08a7d68c", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_stacksize'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 1720, "wires": [ [ "e2f8fdd47bdd1b66" ] ] }, { "id": "4cfada2de1c5bb74", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "if (msg.enabled == true){\n msg.enabled = false\n}\nelse{\n msg.enabled = true\n}\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 570, "y": 1660, "wires": [ [ "84608db962fd9932" ] ] }, { "id": "84608db962fd9932", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "149e2e46b9623a2d" ], "x": 675, "y": 1660, "wires": [] }, { "id": "e89c16809f8a5f1c", "type": "python3-function", "z": "1613373abaf77a2c", "name": "gphoto", "func": "\nfrom OpenScan import camera, save, load_str\n\nif load_str('camera') == 'gphoto':\n if camera('/gphoto_init') == 200:\n if camera('/gphoto_test') == 200:\n save('status_internal_cam','--READY--')\n return msg\nif load_str('camera') == 'external':\n save('status_internal_cam','--READY--')", "outputs": 1, "x": 490, "y": 100, "wires": [ [] ] }, { "id": "917a194be245384a", "type": "link in", "z": "1613373abaf77a2c", "name": "enable projectname", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 540, "wires": [ [ "f4b3112a9ec6c487" ] ] }, { "id": "65cef204b16f8741", "type": "link in", "z": "1613373abaf77a2c", "name": "enable shutter", "links": [ "2d76e5617f13cd6c", "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 580, "wires": [ [ "84d6b96c8ebaac96" ] ] }, { "id": "2aea1727dbea76ce", "type": "link in", "z": "1613373abaf77a2c", "name": "enable cropx", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 620, "wires": [ [ "9c6b48b7b4cc4e1a" ] ] }, { "id": "4f212b44aa487945", "type": "link in", "z": "1613373abaf77a2c", "name": "enable cropy", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 660, "wires": [ [ "c470fd0b15356206" ] ] }, { "id": "6d1e12f51f9af0b6", "type": "link in", "z": "1613373abaf77a2c", "name": "start camchk", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 55, "y": 100, "wires": [ [ "12f1399b240830bf" ] ] }, { "id": "8ebd1dcb5db156ed", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 2, "width": 6, "height": 1, "name": "", "label": "Current Status:", "format": " {{msg.payload}} ", "layout": "row-spread", "className": "", "x": 320, "y": 160, "wires": [] }, { "id": "94a7aec739f9266b", "type": "function", "z": "1613373abaf77a2c", "name": "loadS", "func": "var file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\n\nif (data === 'no camera found'){\n msg.color = 'red'\n}\n\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 160, "wires": [ [ "8ebd1dcb5db156ed" ] ] }, { "id": "2415272f42ce468c", "type": "link in", "z": "1613373abaf77a2c", "name": "start status", "links": [ "6c6ef2255a7d39e5" ], "x": 55, "y": 160, "wires": [ [ "94a7aec739f9266b" ] ] }, { "id": "a1e14624058e74cd", "type": "link in", "z": "1613373abaf77a2c", "name": "start routine settings", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 55, "y": 500, "wires": [ [ "f4b3112a9ec6c487", "107a030938cbfea9", "84d6b96c8ebaac96", "9c6b48b7b4cc4e1a", "c470fd0b15356206", "9e30e33a1520fee0", "79ecb889f7113405" ] ] }, { "id": "1daf9e3a5bd5ab48", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "global.set('flag_pw', true)\nglobal.set('flag', true)\nmsg.enabled = true\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 920, "wires": [ [ "fb13752beddee9f2" ] ] }, { "id": "6d15f717d5a11002", "type": "function", "z": "1613373abaf77a2c", "name": "disable", "func": "msg.enabled = false\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 840, "wires": [ [ "e9b13dfd9f8d3711" ] ] }, { "id": "d14bbbb446d45e39", "type": "link in", "z": "1613373abaf77a2c", "name": "preview", "links": [ "f20da2fc4978b7bf" ], "x": 135, "y": 1260, "wires": [ [ "95439678bb2df2a2" ] ] }, { "id": "db7eea74d3bf892b", "type": "ui_toast", "z": "1613373abaf77a2c", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Finish", "cancel": "2nd pass", "raw": false, "className": "", "topic": "", "name": "", "x": 510, "y": 880, "wires": [ [ "0b8661103366f834" ] ] }, { "id": "0b8661103366f834", "type": "python3-function", "z": "1613373abaf77a2c", "name": "continue", "func": "from os import system\nfrom os.path import isfile\n\n\nif msg['payload'] == '2nd pass':\n msg['enabled'] = True\n return msg,None\n\nbasepath = '/home/pi/OpenScan/'\ntemppath = basepath + 'tmp/tmp.jpg'\nzippath = basepath + 'tmp/tmp.zip'\nprojectcode = msg['projectcode']\n\nsystem('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\n\nmsg['path'] = basepath + 'scans/' + projectcode + '.zip'\n\nif isfile(zippath):\n system('rm ' + zippath)\n\nreturn None, msg", "outputs": 2, "x": 660, "y": 920, "wires": [ [ "431f917c2541ae48", "579f2211199fd6ab", "c433515042ba01b5" ], [ "1daf9e3a5bd5ab48", "579f2211199fd6ab", "c433515042ba01b5" ] ] }, { "id": "79ecb889f7113405", "type": "python3-function", "z": "1613373abaf77a2c", "name": "inactive", "func": "from requests import get\nfrom OpenScan import load_int\n\ntimeout = load_int('timeout_ringlight')\n\nmsg['cmd'] = 'get'\n\ntry:\n flask = 'http://127.0.0.1:1312/ping'\n r = get(flask, params=msg)\n\n idle = float(r.text.split(\":\")[1].split('}')[0])\n\n msg['payload'] = idle\n\n if idle > timeout:\n return msg,msg\nexcept:\n pass\n\nreturn None,msg", "outputs": 2, "x": 200, "y": 740, "wires": [ [ "9e30e33a1520fee0" ], [ "8d7e04531c34f349" ] ] }, { "id": "8d7e04531c34f349", "type": "delay", "z": "1613373abaf77a2c", "name": "", "pauseType": "delay", "timeout": "30", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 200, "y": 780, "wires": [ [ "79ecb889f7113405" ] ] }, { "id": "c433515042ba01b5", "type": "ui_button", "z": "1613373abaf77a2c", "name": "pause", "group": "7aaf184330605300", "order": 22, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-pause", "payload": " ", "payloadType": "str", "topic": "Scan paused", "topicType": "str", "x": 810, "y": 1040, "wires": [ [ "63db399d8ac2acb6" ] ] }, { "id": "63db399d8ac2acb6", "type": "python3-function", "z": "1613373abaf77a2c", "name": "pause", "func": "from OpenScan import load_str, save\n\nstatus = load_str('status_internal_cam')\n\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\n return\n\nif status == 'Routine-paused':\n save('status_internal_cam', 'Routine-continue')\nelse:\n save('status_internal_cam', 'Routine-paused')", "outputs": 1, "x": 930, "y": 1040, "wires": [ [] ] }, { "id": "44c598049cd533fd", "type": "link in", "z": "1613373abaf77a2c", "name": "crop", "links": [ "f358de1e64b491bb" ], "x": 595, "y": 640, "wires": [ [ "fecf5cff888bb570", "0ee4950bd21498bd" ] ] }, { "id": "ea54fcc2.cfcc2", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "get dirs", "func": "from glob import glob\nimport os\nfrom zipfile import ZipFile\nfrom datetime import datetime\nfrom PIL import Image\n\ndef set_stats(stat):\n try:\n with open(directory+set[:-4]+\"/\"+stat,\"r\") as file:\n stat=file.read()\n except:\n stat=\"\"\n return stat\n\ntable=[]\ndirectory=\"/home/pi/OpenScan/scans/\"\n\nfor d in glob(directory+\"*.zip\"):\n set=os.path.basename(d)\n\n try:\n with ZipFile(d, 'r') as f:\n photos = len(f.namelist())\n \n if not os.path.isfile(directory + 'preview/' + os.path.basename(d)[:-4]+'.jpg'):\n image = f.open(f.namelist()[int(photos/2)])\n img = Image.open(image)\n width, height = img.size\n width_factor = width/300\n height_factor = height/295\n if height_factor>=width_factor and height_factor > 1:\n new_size=(int(width/height_factor), int(height/height_factor))\n img = img.resize(new_size)\n elif height_factor 1:\n new_size=(int(width/width_factor),int(height/width_factor))\n img = img.resize(new_size)\n img.save(directory + 'preview/' + os.path.basename(d)[:-4] +'.jpg')\n list=[]\n for fi in f.filelist:\n list.append(f.getinfo(fi.filename).date_time)\n \n duration = str(datetime(*max(list)) - datetime(*min(list)))\n \n size = float(int(float(os.path.getsize(d))/100000))/10\n size_full= os.path.getsize(d)\n status=set_stats(\"status\")\n expiration=set_stats(\"expiration\")\n download=set_stats(\"download\")\n \n if len(download)!=0:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Download\":\"RESULT\",\n \"Size_full\":size_full,\n \"Duration\":duration,\n })\n else:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Size_full\":size_full,\n \"Duration\":duration,\n\n })\n except:\n pass\n\nmsg['payload']=table\nmsg['topic']=\"\"\nreturn msg", "outputs": 1, "x": 480, "y": 180, "wires": [ [ "b9a3a0f9.bcbea", "f3662f8c7d3d7a2d" ] ] }, { "id": "2f4c0f98.dee2", "type": "link in", "z": "4981d84ef1a366d1", "name": "filelist", "links": [ "960912e90ba5b5bc", "a4f09e25.02569", "ed35109311335099", "fb13752beddee9f2", "50eeb3e362f9027f" ], "x": 355, "y": 140, "wires": [ [ "ea54fcc2.cfcc2" ] ] }, { "id": "b9a3a0f9.bcbea", "type": "ui_table", "z": "4981d84ef1a366d1", "group": "b5fdd57b.15eda8", "name": "", "order": 1, "width": 13, "height": 7, "columns": [ { "field": "Date", "title": "Date", "width": "150", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Name", "title": "Name", "width": "150", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Photos", "title": "Photos", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Duration", "title": "ΔT", "width": "60", "align": "left", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Size", "title": "Size", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Status", "title": "Status", "width": "140", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } } ], "outputs": 1, "cts": true, "x": 610, "y": 180, "wires": [ [ "50710948.71c308", "4082b136.dae18", "834046a4.647938", "0c387c0291d6c131" ] ] }, { "id": "952ce286.4ffd4", "type": "ui_text", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "order": 3, "width": 6, "height": 1, "name": "Status", "label": "Status", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 250, "y": 60, "wires": [] }, { "id": "d4383424.7807c8", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "upload", "func": "import os\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\nfrom subprocess import getoutput\n\nbasedir = '/home/pi/OpenScan/'\n\nif load_str(\"feedback_terms\")==\"False\":\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic'] = 'OpenScanCloud - Terms of Use'\n return None,msg\n\nmsg = msg['payload']\n\ndef upload(filelist, ulinks):\n pid = getoutput('pidof curl')\n if pid != \"\":\n os.system('kill ' + pid)\n\n i = 0\n for file in filelist:\n link = ulinks[i]\n save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\n cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\"' + file + '\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\n i = i+1\n os.system(cmd)\n\n########\nif not os.path.isfile(basedir + 'settings/token'):\n msg['flag'] = True\n save('status_cloud', 'please enter token first')\n return msg\nwith open(basedir + 'settings/token', 'r') as file:\n token = file.read().strip('\\n')\n\n########\nr = OpenScanCloud('getTokenInfo', {'token':token})\n\nif r.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n save('status_cloud', 'invalid/missing token')\n return None,msg\nelif r.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nmsg1 = r.json()\n\n########\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\n msg['flag'] = True\n save('status_cloud', 'limit(s) exceeded')\n return msg\n\n########\ntemp = OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\nif temp.status_code not in (200,401):\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nif temp.status_code != 401:\n temp = temp.json()\n if 'status' in temp:\n if temp['status'] != 'created':\n save('status_cloud','already exists')\n with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\n file.write(temp['status'])\n return msg\n#####\n\nmsg2={}\nmsg2['token'] = token\nmsg2['parts'] = 1\nmsg['partslist']=[]\n\n#######\nsize_to_split = load_int('osc_splitsize')\n\nif msg['Size_full'] > size_to_split:\n tempdir = basedir + 'tmp/split/'\n if os.path.isdir(tempdir):\n os.system('rm -r ' + tempdir)\n os.mkdir(tempdir)\n save('status_cloud', 'zipping files, please wait ...')\n cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\n os.system(cmd)\n save('status_cloud', 'zip done')\n list = os.listdir(tempdir)\n for l in list:\n msg['partslist'].append(tempdir + l)\n msg['partslist'].sort()\n msg2['parts']=len(msg['partslist'])\nelse:\n msg['partslist'] = [basedir + 'scans/' +msg['Set']]\n\n#######\nmsg2['photos'] = msg['Photos']\nmsg2['filesize'] = msg['Size_full']\nmsg2['project'] = msg['Set']\n\nr = OpenScanCloud('createProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nmsg1 = r.json()\n\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\n os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\n file.write('prepared')\n\nsave('status_cloud', 'uploading')\nupload(msg['partslist'], msg1['ulink'])\n\nr = OpenScanCloud('startProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Upload failed'\n msg['payload'] = 'please try again'\n save('status_cloud', 'upload failed')\n return None,msg\n\nsave('status_cloud', 'uploaded')\n\nsave('status_cloud', 'project started')\n\ntry:\n os.system('rm -r ' + tempdir)\nexcept:\n pass\n\nreturn msg", "outputs": 2, "x": 530, "y": 460, "wires": [ [ "9a132ab1.b21658" ], [ "3d16b3789632784d", "9a132ab1.b21658" ] ] }, { "id": "50710948.71c308", "type": "change", "z": "4981d84ef1a366d1", "name": "set", "rules": [ { "t": "set", "p": "set", "pt": "global", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 750, "y": 180, "wires": [ [ "ada1b6f7cccc9344", "85839a17fb7b58b9" ] ] }, { "id": "834046a4.647938", "type": "ui_text", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "order": 4, "width": 6, "height": 1, "name": "Set", "label": "Set:", "format": "{{msg.payload.Name}}", "layout": "row-spread", "className": "", "x": 750, "y": 220, "wires": [] }, { "id": "9a132ab1.b21658", "type": "change", "z": "4981d84ef1a366d1", "name": "flag.true", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 780, "y": 460, "wires": [ [ "8689e938.dd9e38" ] ] }, { "id": "3c67e97b.9d19a6", "type": "function", "z": "4981d84ef1a366d1", "name": "enable", "func": "if (global.get('flag') === false){\n msg.enabled = false\n msg.color=\"white\"\n}\nelse{\n msg.enabled = true\n msg.color=\"red\"\n \n}\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 130, "y": 340, "wires": [ [ "7a93d1e18254685c", "e434ef42bd6b92e8", "d5d840183025d91b", "ab9e90ab5a53a0dd", "478994f671a3907d" ] ] }, { "id": "bfc01f26.c32cf", "type": "change", "z": "4981d84ef1a366d1", "name": "flag.false", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 420, "y": 500, "wires": [ [ "f20f2dbc.0f123" ] ] }, { "id": "b33d604c.5f1a6", "type": "link in", "z": "4981d84ef1a366d1", "name": "enable cloud", "links": [ "4082b136.dae18", "8689e938.dd9e38", "bd75f33b8a57c522", "e9b13dfd9f8d3711", "f20f2dbc.0f123", "fb13752beddee9f2" ], "x": 35, "y": 340, "wires": [ [ "3c67e97b.9d19a6" ] ] }, { "id": "f6bd1a04.470838", "type": "change", "z": "4981d84ef1a366d1", "name": "set", "rules": [ { "t": "set", "p": "payload", "pt": "msg", "to": "set", "tot": "global" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 410, "y": 460, "wires": [ [ "d4383424.7807c8" ] ] }, { "id": "4082b136.dae18", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "links": [ "b33d604c.5f1a6", "87574a42938afec4" ], "x": 715, "y": 140, "wires": [] }, { "id": "f20f2dbc.0f123", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 515, "y": 500, "wires": [] }, { "id": "8689e938.dd9e38", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 875, "y": 460, "wires": [] }, { "id": "15de0ebb.616d61", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 380, "wires": [ [ "a7d89487.ee8858" ] ] }, { "id": "a7d89487.ee8858", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\ntry:\n os.remove(dir+msg['Set'])\n shutil.rmtree(dir+msg['Set'][:-4])\nexcept:\n pass\nreturn msg", "outputs": 1, "x": 690, "y": 380, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "a4f09e25.02569", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "links": [ "2f4c0f98.dee2", "c20357dd.374108", "e9aab326.a6896", "edd22cc7.befe1", "19b81967.49db87", "8ee1b3bb.7b0b3", "d5246b3cc796afc6" ], "x": 775, "y": 360, "wires": [] }, { "id": "7a93d1e18254685c", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "92c98e6ce7cd25f9" ], "x": 235, "y": 500, "wires": [] }, { "id": "4d99c601c9881680", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "refresh", "func": "from time import sleep\nimport os\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\n\nbasepath = '/home/pi/OpenScan/scans/'\n\nif load_bool(\"terms\")==False:\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic']='OpenScanCloud - Terms of Use'\n return None,msg\n\nsave('status_cloud','refreshing')\ntoken = load_str('token')\n\ntest = OpenScanCloud('getTokenInfo',{'token':token})\nif test.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n return None,msg\nelif test.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nstats = test.json()\nfor i in stats:\n save('osc_'+i, stats[i])\n pass\n\nmsg={}\nprojects = []\nfor i in os.listdir(basepath):\n if i == 'preview':\n continue\n if os.path.isdir(basepath + i):\n if os.path.isfile(basepath + i + '/status'):\n with open(basepath + i + '/status', 'r') as file:\n status = file.read().strip('\\n')\n if status in ['expired', 'processing done', 'processing failed']:\n continue\n projects.append(i)\n\nfor p in projects:\n r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\n if r.status_code == 200:\n answer = r.json()\n if answer == {}:\n os.system('rm -r ' + basepath + p)\n else:\n with open(basepath + p + '/status', 'w+') as file:\n file.write(answer['status'])\n with open(basepath + p + '/download', 'w+') as file:\n file.write(answer['dlink'])\n\nmsg['list'] = projects\nsleep(0.5)\nsave('status_cloud','ready')\nreturn msg, None\n", "outputs": 2, "x": 320, "y": 180, "wires": [ [ "ea54fcc2.cfcc2", "b42e061fb1f1f3d7" ], [ "6434e713f088012b" ] ] }, { "id": "372e95797a3f2f3b", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "limit :)", "func": "from time import sleep\n\nmsg2={}\nmsg2['enabled'] = True\n\nmsg['enabled'] = False\nnode.send(msg)\n\nwait = 15\n\nfor i in range (wait):\n msg['text'] = ' ('+ str(wait - i)+')'\n node.send(msg)\n\nmsg['enabled'] = True\nmsg['text']=\"\"\n\n\nreturn msg", "outputs": 1, "x": 90, "y": 220, "wires": [ [ "573edbfdb7500ddc" ] ] }, { "id": "573edbfdb7500ddc", "type": "delay", "z": "4981d84ef1a366d1", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 230, "y": 220, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "dacb1f078b624e10", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 340, "wires": [ [ "c8d65cc7c2ff7c36" ] ] }, { "id": "92c98e6ce7cd25f9", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "7a93d1e18254685c", "bd75f33b8a57c522" ], "x": 35, "y": 180, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "3d16b3789632784d", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 770, "y": 500, "wires": [ [] ] }, { "id": "6434e713f088012b", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 470, "y": 220, "wires": [ [] ] }, { "id": "c8d65cc7c2ff7c36", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\nfor i in os.listdir(dir):\n if os.path.isdir(dir + i):\n shutil.rmtree(dir + i)\n else:\n os.remove(dir + i)\n\nreturn msg", "outputs": 1, "x": 690, "y": 340, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "f4e9a4bd79b4221f", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 340, "wires": [ [ "dacb1f078b624e10" ] ] }, { "id": "2806bf08ea21216d", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.Set=global.get('set')['Set']\nmsg.payload = 'Are you sure to delete ' + msg.Set + '?'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 380, "wires": [ [ "15de0ebb.616d61" ] ] }, { "id": "61990987acd0f263", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "6c6ef2255a7d39e5" ], "x": 45, "y": 60, "wires": [ [ "51579603bce21e98" ] ] }, { "id": "e8e488a6dd5d0b33", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "Download", "order": 6, "width": 3, "height": 1, "format": "\n
Download\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 880, "y": 260, "wires": [ [] ] }, { "id": "0c387c0291d6c131", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.download = '/scans/' + String(msg.payload.Set)\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 750, "y": 260, "wires": [ [ "e8e488a6dd5d0b33" ] ] }, { "id": "e5f38b4a07a5e278", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 655, "y": 220, "wires": [ [ "834046a4.647938" ] ] }, { "id": "e434ef42bd6b92e8", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "upload2", "order": 7, "width": 3, "height": 1, "format": "upload", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 460, "wires": [ [ "f6bd1a04.470838", "bfc01f26.c32cf" ] ] }, { "id": "c46e10b9c201913e", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "refresh", "order": 1, "width": 3, "height": 1, "format": "refresh{{msg.text}}", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 160, "y": 180, "wires": [ [ "372e95797a3f2f3b", "4d99c601c9881680" ] ] }, { "id": "d5d840183025d91b", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "del set", "order": 9, "width": 2, "height": 1, "format": "delete set", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 380, "wires": [ [ "2806bf08ea21216d" ] ] }, { "id": "ab9e90ab5a53a0dd", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "del ", "order": 10, "width": 2, "height": 1, "format": "delete all", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 340, "wires": [ [ "f4e9a4bd79b4221f" ] ] }, { "id": "478994f671a3907d", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "combine", "order": 8, "width": 2, "height": 1, "format": "combine", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 540, "wires": [ [ "51bfd0fb7b1d292e" ] ] }, { "id": "189c1eed09624a7b", "type": "function", "z": "4981d84ef1a366d1", "name": "combine", "func": "combine = global.get('combine')\ncombine_set = global.get('set').Set\n\nif (combine === true && global.get('combine_set') !== combine_set){\n msg.set1 = global.get('combine_set')\n msg.set2 = combine_set\n global.set('combine', false)\n msg.topic = 'Combine the following two sets:'\n msg.payload = msg.set1 + '
' + msg.set2 + '
FILES WILL BE MERGED INTO ON FILE!'\n return msg\n}\nglobal.set('combine_set' , combine_set)\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 580, "wires": [ [ "1493398979a63775" ] ] }, { "id": "51bfd0fb7b1d292e", "type": "function", "z": "4981d84ef1a366d1", "name": "combine", "func": "global.set('combine', true)\ncombine_set = global.get('set').Set\nmsg.topic = 'Merge two sets into one (can not be undone)!'\nmsg.payload = combine_set\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 420, "y": 540, "wires": [ [] ] }, { "id": "da325be8e74179be", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "combine", "func": "from os.path import getsize\nfrom shutil import copy\nfrom os import rename, remove\nimport zipfile as z\nfrom OpenScan import save\n\nfrom time import sleep\n\nif msg['payload'] != 'OK':\n return\n\nbasepath = '/home/pi/OpenScan/scans/'\ntmp1 = basepath + msg['set1']\ntmp2 = basepath + msg['set2']\n\nif getsize(tmp1) > getsize(tmp2):\n set1 = tmp1\n set2 = tmp2\nelse:\n set1 = tmp2\n set2 = tmp1\n\nzips = [set1, set2]\n\nwith z.ZipFile(set1, 'a') as z1:\n z2 = z.ZipFile(set2, 'r')\n i = 0\n for n in z2.namelist():\n i += 1\n n2 = n\n save('status_cloud','writing ' + str(i) + '/' + str(len(z2.namelist())))\n while 'X'+n in z1.namelist():\n n = 'X' + n\n z1.writestr('X'+n, z2.open(n2).read())\nsave('status_cloud','ready')\n\nos.rename(set1, set1[:-4] + 'X.zip')\nos.remove(set2)\n\nreturn msg", "outputs": 1, "x": 560, "y": 580, "wires": [ [ "ed35109311335099" ] ] }, { "id": "ed35109311335099", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "2f4c0f98.dee2" ], "x": 655, "y": 580, "wires": [] }, { "id": "1493398979a63775", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "Combine", "x": 420, "y": 580, "wires": [ [ "da325be8e74179be" ] ] }, { "id": "ada1b6f7cccc9344", "type": "link out", "z": "4981d84ef1a366d1", "name": "combine", "mode": "link", "links": [ "6dd356510c446cf4" ], "x": 835, "y": 180, "wires": [] }, { "id": "6dd356510c446cf4", "type": "link in", "z": "4981d84ef1a366d1", "name": "combine", "links": [ "ada1b6f7cccc9344" ], "x": 175, "y": 580, "wires": [ [ "189c1eed09624a7b" ] ] }, { "id": "b42e061fb1f1f3d7", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "397ab7f44b893c89" ], "x": 435, "y": 140, "wires": [] }, { "id": "b99505440832439f", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "diskspace", "func": "from subprocess import getoutput\nfrom OpenScan import load_int\n\ndiskspace_threshold = load_int('diskspace_threshold')\n\ndiskspace = getoutput('df -h / | awk \"{print $5}\"').split('\\n')[1]\n\navailable = int(float(diskspace.replace(' ','').split('G')[2])*1000)\n\n\nif available < diskspace_threshold:\n msg['topic'] = 'Low diskspace remaining! ('+str(available)+'MB)' \n msg['payload'] = 'Please delete some/all locally stored files.'\n msg['color'] = 'red'\n return msg\n", "outputs": 1, "x": 800, "y": 100, "wires": [ [ "92047434f8e9f927" ] ] }, { "id": "92047434f8e9f927", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 950, "y": 100, "wires": [ [] ] }, { "id": "f3662f8c7d3d7a2d", "type": "delay", "z": "4981d84ef1a366d1", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "minute", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "allowrate": false, "outputs": 1, "x": 650, "y": 100, "wires": [ [ "b99505440832439f" ] ] }, { "id": "51579603bce21e98", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "read", "func": "from OpenScan import load_str\nfrom os import listdir, path\n\nstatus = load_str('status_cloud')\n\nif status[0:9] == 'uploading':\n progress = load_str('status_uploadprogress')[-6:]\n if progress[-1:] == '%':\n status = status + ' (' + progress + ')'\n\nif status[0:7] == 'zipping':\n path1 = '/home/pi/OpenScan/tmp/split/'\n files = listdir(path1)\n size1 = 0\n for file in files:\n size1 += path.getsize(path1+file)\n size2 = path.getsize('/home/pi/OpenScan/scans/'+ files[0][:-2])\n \n status = 'zipping files (' + str(float(int(1000*size1/size2))/10) + '%)'\n\nmsg['status'] = status\nreturn msg\n", "outputs": 1, "x": 130, "y": 60, "wires": [ [ "952ce286.4ffd4" ] ] }, { "id": "9a5baae623355f9d", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "preview", "order": 5, "width": 6, "height": 6, "format": "
\n\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 1020, "y": 220, "wires": [ [] ] }, { "id": "85839a17fb7b58b9", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "preview", "func": "from time import time\nimport os\n\npath = '/home/pi/OpenScan/scans/preview/'\nimage = os.path.basename(msg['payload']['Set'])[:-4] +'.jpg'\n\nmsg['payload']=\"/scans/preview/\" + image +\"?ts=\"+str(int(time()*10))\nreturn msg", "outputs": 1, "x": 880, "y": 220, "wires": [ [ "9a5baae623355f9d" ] ] }, { "id": "45058bfcf047e8cc", "type": "inject", "z": "4981d84ef1a366d1", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 100, "y": 120, "wires": [ [] ] }, { "id": "40dee936a9abac0d", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "SSH", "tooltip": "", "group": "4fe6b4c0ade0938a", "order": 3, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 390, "y": 340, "wires": [ [ "dc354c54078ca607" ] ] }, { "id": "4fd9bb53fdb51a25", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Samba", "tooltip": "", "group": "4fe6b4c0ade0938a", "order": 4, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "test2", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 400, "y": 380, "wires": [ [ "b0aa8ffae5a3578a" ] ] }, { "id": "dc354c54078ca607", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "ssh", "func": "from OpenScan import load_bool, save\n\nstate = msg['payload']\n\nif state != load_bool('ssh'):\n save('ssh', state)\n\nif state == True:\n os.system('/etc/init.d/ssh start')\nelse:\n os.system('/etc/init.d/ssh stop')", "outputs": 1, "x": 530, "y": 340, "wires": [ [] ] }, { "id": "52858b4eceacc902", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "", "group": "4fe6b4c0ade0938a", "order": 2, "width": 6, "height": 1, "passthru": false, "label": "Terms Of Use", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 120, "y": 300, "wires": [ [ "f99ec8781a33ec7d" ] ] }, { "id": "595153429adef33b", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Wifi", "group": "0fe66c9190b8a87c", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Network Settings

Hostname

The device can be accessed through any browser in the same network. Therefore, you can either enter the device's IP address or the given hostname. The standard name is 'openscan' but it is highly recommended to change the name, when using multiple devices (e.g. 'openscan1', 'openscan2' ...)

Select Wifi

After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: ISO 3166 Country Code on Wikipedia

Search Wifi

You can manually refresh the list of available networks by pressing this button.

Reset Wifi

Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see glennklockwood.com for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 590, "y": 120, "wires": [ [ "f304680180a23479" ] ] }, { "id": "7dc39bd847d16ded", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Agree", "cancel": "Disagree", "raw": true, "className": "", "topic": "", "name": "", "x": 410, "y": 300, "wires": [ [ "5f849178998d9082" ] ] }, { "id": "02858034e17b827f", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "General", "group": "4fe6b4c0ade0938a", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

General Settings

Terms Of Use

In order to use the OpenScanCloud, please read the terms of use as files will be transmitted from your device to the OpenScan Servers.

SSH

SSH can be used to access the Raspberry Pi and modify core files of the operating system. Please deactivate, if you do not want to use this feature.

If you want to use it, the default user is pi, password: raspberry. Please change the password immediately. 

Samba

Samba s a network local file sharing server, which allows accessing the Raspberry Pi's file system through the explorer (and other programs like FileZilla). You can use it to transfer custom photo sets to the device in order to use the OpenScanCloud. Therefore, you need to transfer the zip file containing your photos to the following folder /OpenScan/scans/

You can access the Raspberry Pis file system by inserting the following line into your Windows explorer: 

\\\\OpenScan/PiShare/OpenScan/scans/

username: pi, password: raspberry

Please deactivate the local file sharing if you do not intend to use it

Advanced Settings

Enable a ton of additional settings, which should be changed only if you know what you are doing ;)

Model

Device model you are using: OpenScan Mini or OpenScan Classic. Setting the device affects the settings of the motor (gear ratio, acceleration, speed). You can change those values manually in the advanced settings.

Camera

A wide range of camera modules is supported (Pi camera v1.3, v2.1, HQ, Arducam IMX519, IMX290, IMX378, OV9281). If you encounter any issues with those models, please check the orientation of the camera ribbon cable and its connectors.

DSLR (gphoto) - connect a wide range of DSLR cameras to the device through USB. See GPhoto for a full list of supported devices.

External camera - triggering any camera through an isolated GPIO signal on the front side of the pi shield.

Shutdown/Reboot

Always use the shutdown button before you power off your Raspberry Pi.

Restore Default Settings

In case you want to restore the default settings

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 740, "y": 240, "wires": [ [ "f304680180a23479" ] ] }, { "id": "675d4933a44ae6b5", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Pinout", "group": "644b3bcc903d46ca", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Pinout

ONLY CHANGE THE PINOUT IF YOU ARE ABSOLUTELY SURE! CHANGES CAN DAMAGE THE RASPBERRY PI AND ANY PERIPHERALS!


", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 430, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "b0aa8ffae5a3578a", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "smb", "func": "from OpenScan import load_bool, save\n\nstate = msg['payload']\n\nif state != load_bool('smb'):\n save('smb', state)\nif state == True:\n os.system('/etc/init.d/smbd start')\nelse:\n os.system('/etc/init.d/smbd stop')\n\n\n", "outputs": 1, "x": 530, "y": 380, "wires": [ [] ] }, { "id": "cc3cb10f2ea3f8b8", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "blink Light1", "func": "import RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nfrom OpenScan import ringlight\nfrom time import sleep\n\ndelay = 0.1\nringlight(2,False)\n\nfor i in range (5):\n ringlight(1,True)\n sleep(delay)\n ringlight(1,False)\n sleep(delay)", "outputs": 1, "x": 290, "y": 760, "wires": [ [] ] }, { "id": "d114f4d4d7f31981", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "reboot", "func": "import os\nfrom time import sleep\nsleep(1.5)\nimport RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nos.system('reboot -h')\n", "outputs": 1, "x": 270, "y": 720, "wires": [ [] ] }, { "id": "79181ad3b56d5c62", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "4fe6b4c0ade0938a", "order": 7, "width": 2, "height": 1, "name": "", "label": "Model", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 730, "y": 620, "wires": [] }, { "id": "4d81bd138733c410", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "4fe6b4c0ade0938a", "order": 9, "width": 2, "height": 1, "name": "", "label": "Camera", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 840, "y": 420, "wires": [] }, { "id": "80b579a4220e5c23", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "model", "label": "", "tooltip": "", "place": "Select option", "group": "4fe6b4c0ade0938a", "order": 8, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Please Select", "value": "None", "type": "str" }, { "label": "OpenScan Mini", "value": "OSMini", "type": "str" }, { "label": "OpenScan Classic", "value": "OSClassic", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 390, "y": 620, "wires": [ [ "896242c5a7e50fa7" ] ] }, { "id": "a2c1dba3e67be015", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "Camera", "label": "", "tooltip": "", "place": "Select option", "group": "4fe6b4c0ade0938a", "order": 10, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Pi Cam v1 - 5mp", "value": "ov5647", "type": "str" }, { "label": "Pi Cam v2 - 8mp", "value": "imx219", "type": "str" }, { "label": "Pi Cam HQ - 12.3mp", "value": "imx477", "type": "str" }, { "label": "Arducam IMX519 - 16mp", "value": "imx519", "type": "str" }, { "label": "IMX290 a", "value": "imx290a", "type": "str" }, { "label": "IMX290 b", "value": "imx290b", "type": "str" }, { "label": "IMX378", "value": "imx378", "type": "str" }, { "label": "OV9281", "value": "ov9281", "type": "str" }, { "label": "DSLR (gphoto)", "value": "gphoto", "type": "str" }, { "label": "USB Webcam", "value": "usb_webcam", "type": "str" }, { "label": "External Camera", "value": "external", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 400, "y": 420, "wires": [ [ "4058a31e942e8f95", "6d68cccec646e0a0" ] ] }, { "id": "9cf5d56263caada7", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Motor", "group": "d49a6dfd7fb17096", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Motor Settings

Turntable Mode

Activate turntable mode in order to deactivate the rotor. The routine will only move the turntable and take a given number of photos.

Rotor - Start Angle, Min and Max Angle

Since this version of OpenScan does not have an endstop (yet), it is necessary to tell the device its position when the routine is being started. 0° corresponds to the horizontal (natural) orientation.

After that, the device will equally space the image positions between angle min and angle max.

Rotor/Turntable

Steps per rotation -  defines the number of steps it takes to move the axis 360°. It is defined by A*B*C, where A is the number of steps for one revolution of the given stepper motor (normally 200), B is the microstepping used (normally 16), and C the gear ratio (1 for the turntable and 15 or 5,33 for the OpenScan Mini and Classic respectively)

Delay - time in microseconds between each step of the motor. Lower this value if the movement is too fast

Acceleration - a factor defining how fast the delay time between each step is being changed during acceleration and deceleration phases. Lower this value in order to make the movement smoother.

Acceleration ramp - the number of steps allowed for the acceleration processes. Increase this value, if you want smoother movement.

Manual Angle - Defines the degree value for the manual movement through the arrow buttons in the scan menu

Direction - If needed, reverse the movement (in case the arrow buttons and movement do not correspond). Alternatively, you can flip the motor cable 180° (BUT MAKE SURE TO POWER OFF THE DEVICE!)


", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 430, "y": 120, "wires": [ [ "f304680180a23479" ] ] }, { "id": "72238e6a01d1152c", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "camera", "group": "93aadb71dee6d977", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Camera Settings

Jpeg quality

Value in percent, which usually does not need to be changed.

Downscale Preview

The preview image has to be scaled down depending on your network speed. If you want to have a higher quality preview image, you can increase this value, which defines the maximal width/height value. If the value is too high, the preview window might not update

Image Rotation

Change the image rotation, if needed.

Timeout

Defines the time in seconds, when the libcamera command (used for the camera modules) will timeout. Increase this value, if the camera does not get triggered in each position.

Delay Before/After

A fixed delay in seconds before and/or after a photo is taken. Increase this value when the photos have visual motion blur.

AWBG, Gain, Contrast, Saturation

Under most circumstances, you do not need to touch these values.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 420, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "15a0a2f431ce55c3", "type": "comment", "z": "017bd4e4a428bee5", "name": "General Settings", "info": "", "x": 120, "y": 260, "wires": [] }, { "id": "87a403b9a09aa38d", "type": "comment", "z": "017bd4e4a428bee5", "name": "Network", "info": "", "x": 100, "y": 880, "wires": [] }, { "id": "896242c5a7e50fa7", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "model", "func": "from OpenScan import load_str, save\n\nstate = msg['payload']\nmsg['state'] = state\n\nif state != load_str('model'):\n save('model', state)\n if state == 'OSMini':\n save('rotor_stepsperrotation',48000)\n save('cam_rotation',90)\n save('rotor_anglemin',-70)\n save('rotor_anglemax',20)\n \n\n if state == 'OSClassic':\n save('rotor_stepsperrotation',17067)\n save('cam_rotation',0)\n save('rotor_anglemin',-30)\n save('rotor_anglemax',30)\n\nif state == \"OSMini\":\n msg['crop2'] = 'Crop X (%)'\n msg['crop1'] = 'Crop Y (%)'\nelif state == \"OSClassic\":\n msg['crop1'] = 'Crop X (%)'\n msg['crop2'] = 'Crop Y (%)'\n\nreturn msg", "outputs": 1, "x": 530, "y": 620, "wires": [ [ "f358de1e64b491bb" ] ] }, { "id": "4058a31e942e8f95", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "camera", "func": "from OpenScan import load_str, save\nfrom json import load\nstate = msg['payload']\nstate_old = load_str('camera')\n\nif state_old != state:\n save('camera',state)\n return msg", "outputs": 1, "x": 540, "y": 500, "wires": [ [ "34b685aff2080d31" ] ] }, { "id": "c833f6243a059d83", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "more sets", "label": "Advanced Settings", "tooltip": "", "group": "4fe6b4c0ade0938a", "order": 5, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 400, "y": 660, "wires": [ [ "8be8015931c663cc" ] ] }, { "id": "15fd1c9e5610cb85", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "more sets", "func": "from OpenScan import save\n\nif msg['payload'] != 'OK':\n msg['payload'] = False\n return None,msg\n \nsave('advanced_settings', True)\n\nreturn msg", "outputs": 2, "x": 820, "y": 660, "wires": [ [ "62cd775a1c02dac8" ], [ "c833f6243a059d83" ] ] }, { "id": "74c5c7cd2681045b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "load camera&model", "func": "from OpenScan import load_str, load_bool\n\nmodel = load_str('model')\ncamera = load_str('camera')\nupdate = load_bool('updateable')\nmsg['model'] = model\nmsg['camera'] = camera\nmsg2 = {}\nmsg3 = {}\nmsg4 = {}\n\nif camera in ('imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','gphoto'):\n msg['payload'] = {\"group\":{\"hide\":[\"Scan_Arducam\"],\"show\":[\"Scan_Settings\",\"Scan_Picamera\"]}}\nelif camera in ('imx519'):\n msg['payload'] = {\"group\":{\"hide\":[\"Scan_Picamera\"],\"show\":[\"Scan_Settings\",\"Scan_Arducam\"]}}\nelif camera in ('external'):\n msg['payload'] = {\"group\":{\"hide\":[\"Scan_Arducam\",\"Scan_Picamera\"],\"show\":[\"Scan_Settings\"]}}\n\n\nif model == 'None' or model == '' or camera == 'None' or camera == '':\n msg2['payload']={\"tabs\": {\"hide\": [\"Scan\", \"Files&Cloud\",\"Settings\",\"Update & Info\"]}}\n msg3['payload'] = {\"group\":{\"hide\":[\"OpenScan_Home\"],\"show\":[\"OpenScan_Initialize\"]}}\nelse:\n msg2['payload']={\"tabs\": {\"show\": [\"Scan\", \"Files&Cloud\",\"Settings\",\"Update & Info\"]},\"hide\":{}}\n msg3['payload'] = {\"group\":{\"show\":[\"OpenScan_Home\"],\"hide\":[\"OpenScan_Initialize\"]}}\n\nif update == True:\n msg4['payload'] = {\"group\":{\"show\":[\"OpenScan_Update\"]}}\nelif update == False:\n msg4['payload'] = {\"group\":{\"hide\":[\"OpenScan_Update\"]}}\n\nreturn msg,msg2,msg3,msg4", "outputs": 4, "x": 340, "y": 40, "wires": [ [ "b4db790aad28ba39" ], [ "b4db790aad28ba39" ], [ "b4db790aad28ba39" ], [ "b4db790aad28ba39" ] ] }, { "id": "b4db790aad28ba39", "type": "ui_ui_control", "z": "017bd4e4a428bee5", "name": "change visibility", "events": "all", "x": 600, "y": 40, "wires": [ [] ] }, { "id": "eb8ccf2786ea3d63", "type": "inject", "z": "017bd4e4a428bee5", "name": "1s_repeater", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "1", "crontab": "", "once": true, "onceDelay": "2", "topic": "", "payload": "", "payloadType": "date", "x": 150, "y": 40, "wires": [ [ "74c5c7cd2681045b", "9b756a1f9b0e7317" ] ] }, { "id": "9b756a1f9b0e7317", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "load advanced", "func": "from OpenScan import load_bool\n\nif load_bool('advanced_settings') == False:\n msg['payload']={\"group\":{\"hide\":[\"Settings_Camera\",\"Settings_Motor\",\"Settings_Pinout\",],\"show\":[\"Settings_General\",\"Settings_Network\",\"Settings_OpenScanCloud\"]}}\nelse:\n msg['payload']={\"group\":{\"hide\":[],\"show\":[\"Settings_General\",\"Settings_Network\",\"Settings_OpenScanCloud\",\"Settings_Camera\",\"Settings_Motor\",\"Settings_Pinout\",]}}\nreturn msg", "outputs": 1, "x": 320, "y": 80, "wires": [ [ "b4db790aad28ba39" ] ] }, { "id": "ca4afadb5b21751f", "type": "comment", "z": "017bd4e4a428bee5", "name": "Info Texts", "info": "", "x": 100, "y": 120, "wires": [] }, { "id": "f393400.d87dcc", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "check ip address", "func": "import socket\nimport subprocess\n\ntestIP = \"8.8.8.8\"\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\ns.connect((testIP, 0))\nipaddr = s.getsockname()[0]\nhost = socket.gethostname()\n\nmsg['ip']=ipaddr\n\nreturn msg", "outputs": 1, "x": 410, "y": 1060, "wires": [ [ "bb789eed.9f73c" ] ] }, { "id": "bb789eed.9f73c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "0fe66c9190b8a87c", "order": 2, "width": 0, "height": 0, "name": "", "label": "Your local IP:", "format": "{{msg.ip}}", "layout": "row-spread", "className": "", "x": 590, "y": 1060, "wires": [] }, { "id": "2a0f9919.4c9a86", "type": "comment", "z": "017bd4e4a428bee5", "name": "OpenScanCloud", "info": "", "x": 120, "y": 1240, "wires": [] }, { "id": "27c6b221c90ed9e1", "type": "exec", "z": "017bd4e4a428bee5", "command": "iwlist wlan0 scan | grep ESSID | sed 's/ESSID://g;s/\"//g;s/^ *//;s/ *$//'", "addpay": false, "append": "", "useSpawn": "false", "timer": "", "winHide": false, "oldrc": false, "name": "scan", "x": 250, "y": 1040, "wires": [ [ "b05cf92302a5c112", "f393400.d87dcc" ], [ "e9677b85856b5873" ], [] ] }, { "id": "b05cf92302a5c112", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "WIFI", "func": "msg['options']=[]\n\nfor i in msg['payload'].split('\\n'):\n if i not in msg['options'] and i!=\"\":\n msg['options'].append(i)\n \nif len(msg['options']) != 0:\n msg['enabled']=True\n\nreturn msg", "outputs": 1, "x": 370, "y": 1020, "wires": [ [ "59c9f67283ba1709" ] ] }, { "id": "da5ddaf4cc25b8c8", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "search", "group": "0fe66c9190b8a87c", "order": 4, "width": 3, "height": 1, "passthru": false, "label": "Search Wifi", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "true", "payloadType": "bool", "topic": "", "topicType": "str", "x": 90, "y": 980, "wires": [ [ "27c6b221c90ed9e1", "51521bc6eb44cde5" ] ] }, { "id": "59c9f67283ba1709", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "", "label": "", "tooltip": "", "place": "Select Wifi", "group": "0fe66c9190b8a87c", "order": 3, "width": 6, "height": 1, "passthru": true, "multiple": false, "options": [], "payload": "", "topic": "", "topicType": "str", "className": "", "x": 520, "y": 980, "wires": [ [ "2bb52656f9554dab" ] ] }, { "id": "b2d7d6a730f7dca6", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Reset Wifi", "group": "0fe66c9190b8a87c", "order": 5, "width": 3, "height": 1, "passthru": false, "label": "Reset Wifi", "tooltip": "", "color": "red", "bgcolor": "", "className": "", "icon": "", "payload": "Delete all prior wifi connections? (You will need to reconnect to the OpenScan device by Ethernet or manually modify the wpa_supplicant.conf)", "payloadType": "str", "topic": "", "topicType": "str", "x": 110, "y": 1140, "wires": [ [ "78985ac6d3bcdf60" ] ] }, { "id": "c3b8faac9ebb2c80", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Reset Wifi", "func": "from time import sleep\n\nif msg['payload']!=\"Yes\":\n return\n\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\nwith open(temp_dir, 'w+') as file:\n file.write('update_config=1\\nctrl_interface=DIR=/var/run/wpa_supplicant\\ncountry=de\\n\\n')\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\nos.system('wpa_cli -i wlan0 reconfigure')\nsleep(3)\nos.system('systemctl restart nodered')\nreturn msg", "outputs": 1, "x": 440, "y": 1140, "wires": [ [] ] }, { "id": "78985ac6d3bcdf60", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 270, "y": 1140, "wires": [ [ "c3b8faac9ebb2c80" ] ] }, { "id": "4f7f49b12c2d2572", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "add Wifi", "func": "from time import sleep\nsleep(0.1)\n\nos.system('wpa_cli -i wlan0 reconfigure')\n\nreturn msg", "outputs": 1, "x": 1320, "y": 1000, "wires": [ [] ] }, { "id": "ebcc98685059b9d4", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "password", "x": 780, "y": 980, "wires": [ [ "68204a14528ab842" ] ] }, { "id": "68204a14528ab842", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "if msg['payload'] == 'Cancel':\n return\n\nmsg['password'] = msg['payload']\nmsg['payload']='Enter country code (ISO 3166-1 alpha-2, see: Wikipedia)'\n\n\nreturn msg", "outputs": 1, "x": 910, "y": 980, "wires": [ [ "852edf901bdec9c5" ] ] }, { "id": "852edf901bdec9c5", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Save", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "country", "x": 1040, "y": 980, "wires": [ [ "1b09d634e3d9357b" ] ] }, { "id": "1b09d634e3d9357b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "modWPA", "func": "if msg['payload'] == 'Cancel':\n return\n\nif len(msg['payload'])!=2:\n msg['payload'] = 'invalid country code'\n return msg,None\n\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\ntemp_dir = '/home/pi/OpenScan/tmp/wpa'\n\ncode = msg['payload'].upper()\nssid = msg['ssid']\npassword = msg['password']\n\nif len(code) != 2:\n msg['topic'] = 'ERROR'\n msg['payload'] = 'invalid country code (see ISO 3166-1 alpha-2)'\n return msg\n\nwith open(wpa_dir, 'r') as file:\n for i in file.readlines():\n if 'country=' in i:\n code_old=i.split('country=')[1][0:2]\n break\n\nwith open(wpa_dir, 'r') as file:\n wpa = file.read()\n if ssid in wpa:\n msg['topic'] = 'ERROR'\n msg['payload'] = 'Network already exists! If you have trouble connecting, please consider resetting the saved Wifi connections.'\n return msg\n wpa=wpa.replace('country=' + code_old, 'country=' + code)\n wpa=wpa + '\\nnetwork={\\n priority=10\\n ssid=\"'+ssid+'\"\\n psk=\"'+password+'\"\\n}\\n'\n\nwith open(temp_dir,'w+') as file:\n file.write(wpa)\nos.system('mv '+temp_dir + ' ' + wpa_dir)\n\nmsg['topic'] = 'Updating Wifi'\nmsg['payload'] = 'reconnecting might take a moment'\nreturn msg,msg\n", "outputs": 2, "x": 1180, "y": 980, "wires": [ [ "03732a7d3b0c95aa" ], [ "4f7f49b12c2d2572" ] ] }, { "id": "03732a7d3b0c95aa", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 1330, "y": 960, "wires": [ [] ] }, { "id": "e97d17c6590138e2", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Cloud-settings", "group": "3b4bd36726be16d5", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

OpenScanCloud

OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at BuyMeACoffee.

The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.

Register

In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.

Enter Token

Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact cloud@openscan.eu

Token

A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests cloud@openscan.eu

Credit (GB)

Each token comes with a given amount of 'credit' which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers. 

IMPORTANT: The credit can be increased at any time by sending a (nice) mail to cloud@openscan.eu

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 620, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "f7bf47e3eec6d736", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 4, "width": 0, "height": 0, "name": "", "label": "Max. Number of Photos:", "format": "{{msg.limit_photos}}", "layout": "row-spread", "className": "", "x": 410, "y": 1380, "wires": [] }, { "id": "b52d91c628b151a4", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 5, "width": 0, "height": 0, "name": "", "label": "Max. Filesize (GB):", "format": "{{msg.limit_filesize}}", "layout": "row-spread", "className": "", "x": 390, "y": 1420, "wires": [] }, { "id": "1969c709ef2fd1d5", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 3, "width": 0, "height": 0, "name": "", "label": "Credit (GB):", "format": "{{msg.credit}}", "layout": "row-spread", "className": "", "x": 370, "y": 1460, "wires": [] }, { "id": "88e92b621d2a3394", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "save", "func": "import requests\nimport os\nfrom OpenScan import save, OpenScanCloud\n\nif msg['payload']!=\"Yes\":\n return None,msg\n\ntry:\n r = OpenScanCloud('getTokenInfo', {'token':msg['token']})\n if r.status_code != 200:\n msg['payload'] = 'Could not verify token'\n return msg \n \n msg1 = r.json()\n \n save('osc_credit',msg1['credit'])\n save('osc_limit_filesize',msg1['limit_filesize'])\n save('osc_limit_photos',msg1['limit_photos'])\n msg1['enabled'] = True\nexcept:\n pass\n\nsave('token',msg['token'])\n \nmsg['payload'] = 'Token verified and saved'\nreturn msg, msg1", "outputs": 2, "x": 750, "y": 1320, "wires": [ [ "76acd48a511a5e3e", "b01581296b94dfcd" ], [ "9c51aa678f16980f" ] ] }, { "id": "76acd48a511a5e3e", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "topic": "", "name": "", "x": 890, "y": 1280, "wires": [ [] ] }, { "id": "5f50ed3f6ba37cef", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "", "label": "Token", "tooltip": "", "group": "3b4bd36726be16d5", "order": 2, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "", "sendOnBlur": true, "className": "", "topicType": "str", "x": 350, "y": 1340, "wires": [ [ "cb62d30728af2968" ] ] }, { "id": "cb62d30728af2968", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Save?", "func": "msg['token'] = msg['payload']\n\nif len(msg['payload'])>=14:\n \n msg[\"payload\"]='Save and verify token: ' + msg['payload']\n return msg\nelse:\n return None,msg", "outputs": 2, "x": 470, "y": 1340, "wires": [ [ "94e503dd2e64d903" ], [ "d859bb39914d4999" ] ] }, { "id": "0dd01eef6e70059e", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "text", "func": "from OpenScan import load_str\n\ntoken = load_str('token')[0:8]\nmsg['payload']= token + '...'\nif len(token)==0:\n msg['payload']=\"enter token\"\nreturn msg", "outputs": 1, "x": 230, "y": 1340, "wires": [ [ "5f50ed3f6ba37cef" ] ] }, { "id": "788fabff98c7973c", "type": "link in", "z": "017bd4e4a428bee5", "name": "token", "links": [ "960912e90ba5b5bc", "b01581296b94dfcd", "d859bb39914d4999", "50eeb3e362f9027f" ], "x": 75, "y": 1340, "wires": [ [ "0dd01eef6e70059e" ] ] }, { "id": "94e503dd2e64d903", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 610, "y": 1320, "wires": [ [ "88e92b621d2a3394" ] ] }, { "id": "d859bb39914d4999", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "788fabff98c7973c" ], "x": 555, "y": 1360, "wires": [] }, { "id": "9c51aa678f16980f", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "links": [ "5d267acc10020091", "397ab7f44b893c89" ], "x": 835, "y": 1360, "wires": [] }, { "id": "397ab7f44b893c89", "type": "link in", "z": "017bd4e4a428bee5", "name": "OSCparameters", "links": [ "960912e90ba5b5bc", "9c51aa678f16980f", "b42e061fb1f1f3d7", "50eeb3e362f9027f" ], "x": 75, "y": 1380, "wires": [ [ "a7fd00943edc380b" ] ] }, { "id": "b01581296b94dfcd", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "links": [ "788fabff98c7973c", "5d267acc10020091" ], "x": 835, "y": 1320, "wires": [] }, { "id": "bf6d941ad307ce22", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 250, "y": 1520, "wires": [ [ "f22dfef37d5de773" ] ] }, { "id": "f22dfef37d5de773", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "import re\n\nif msg['payload'] == 'Cancel':\n return\n\nmail = msg['payload']\nemail_regex = re.compile(r\"[^@]+@[^@]+\\.[^@]+\")\n\nif email_regex.match(mail) != None:\n msg['mail'] = mail\n msg['topic'] = 'OpenScanCloud Registration (2/3)'\n msg['payload'] = 'Enter your first name'\n return msg\nmsg['payload'] = 'invalid input'\nreturn None,msg\n", "outputs": 2, "x": 390, "y": 1520, "wires": [ [ "54602ee49ca022e7" ], [ "1505f3e72f971081" ] ] }, { "id": "1505f3e72f971081", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 530, "y": 1560, "wires": [ [] ] }, { "id": "54602ee49ca022e7", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 530, "y": 1520, "wires": [ [ "f9efcb87b74abbd4" ] ] }, { "id": "510dbe4d76253bd6", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "SUBMIT", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 810, "y": 1520, "wires": [ [ "600b2306caed1640" ] ] }, { "id": "600b2306caed1640", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "import requests\nimport os\nfrom OpenScan import OpenScanCloud\n\nif msg['payload'] == 'Cancel':\n return\n\nmsg['lastname'] = msg['payload']\n\nmsg2 = {}\n\nfor i in ['forename','lastname','mail']:\n msg2[i] = msg[i]\n\nr = OpenScanCloud('requestToken',msg2)\n\nstatus = r.status_code\n\nmsg['topic'] = 'OpenScanCloud Registration - Success'\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\n\nif status != 200:\n msg['topic'] = 'OpenScanCloud Registration - Failed'\n msg['payload'] = 'Registration failed, please try again.'\n\nmsg['status'] = status\n\nreturn msg", "outputs": 1, "x": 950, "y": 1520, "wires": [ [ "bbad1ab5f8f63fb7" ] ] }, { "id": "d34cd203725bac15", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Register", "group": "3b4bd36726be16d5", "order": 7, "width": 2, "height": 1, "passthru": false, "label": "Register", "tooltip": "testtesttest", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "Please enter your email address:", "payloadType": "str", "topic": "Requesting an OpenScanCloud Token", "topicType": "str", "x": 100, "y": 1520, "wires": [ [ "bf6d941ad307ce22" ] ] }, { "id": "bbad1ab5f8f63fb7", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 1090, "y": 1520, "wires": [ [] ] }, { "id": "a7fd00943edc380b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "from OpenScan import load_int\n\nmsg = {}\n\ntry:\n msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\n msg['limit_filesize'] = float(int(load_int('osc_limit_filesize')/10000000))/100\n msg['limit_photos'] = load_int('osc_limit_photos')\n return msg\nexcept:\n pass", "outputs": 1, "x": 230, "y": 1380, "wires": [ [ "f7bf47e3eec6d736", "b52d91c628b151a4", "1969c709ef2fd1d5" ] ] }, { "id": "124459147143ec6a", "type": "comment", "z": "017bd4e4a428bee5", "name": "Motor", "info": "", "x": 90, "y": 1600, "wires": [] }, { "id": "dbd62b91a6c9c412", "type": "comment", "z": "017bd4e4a428bee5", "name": "Camera", "info": "", "x": 90, "y": 2240, "wires": [] }, { "id": "842b6fe016087ce3", "type": "comment", "z": "017bd4e4a428bee5", "name": "Pinout", "info": "", "x": 110, "y": 2860, "wires": [] }, { "id": "8c1a92f2dcc976c7", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "Rotor_delay (ms)", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 14, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "0.2", "step": "0.005", "className": "", "x": 450, "y": 1840, "wires": [ [ "bb54bbdae6690576" ] ] }, { "id": "2647111c06f2055d", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt delay", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 27, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "0.2", "step": "0.005", "className": "", "x": 420, "y": 2080, "wires": [ [ "fb8145a9f8d4f7b2" ] ] }, { "id": "f9b51424edb0491c", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_acc", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 16, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.1", "max": "2", "step": "0.1", "className": "", "x": 420, "y": 1880, "wires": [ [ "ea87ecfd2af3cc7f" ] ] }, { "id": "1ab34b0a78b2c577", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_accramp", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 18, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "100", "max": "5000", "step": "100", "className": "", "x": 440, "y": 1920, "wires": [ [ "249f44c3a87793ba" ] ] }, { "id": "1d4230b3d9b93f63", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotor_stepsperrotation", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 12, "width": 3, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 460, "y": 1800, "wires": [ [ "0bb56b1edb12c2cf" ] ] }, { "id": "2e3222f0aba88040", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 17, "width": 3, "height": 1, "name": "rotor Accramp", "label": "Acceleration ramp", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1880, "wires": [] }, { "id": "9d50311679acf215", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 11, "width": 3, "height": 1, "name": "rotor_Steps per Rotation", "label": "Steps per Rotation", "format": "", "layout": "row-spread", "className": "", "x": 810, "y": 1920, "wires": [] }, { "id": "25d7b4dd2aab8f05", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 15, "width": 3, "height": 1, "name": "rotor Acc", "label": "Acceleration", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 1840, "wires": [] }, { "id": "15682cca9622831f", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 13, "width": 3, "height": 1, "name": "rotor_delay", "label": "Delay", "format": "", "layout": "row-left", "className": "", "x": 770, "y": 1800, "wires": [] }, { "id": "8e2d22042bfcb4e8", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 23, "width": 6, "height": 1, "name": "tt", "label": "TURNTABLE", "format": "", "layout": "row-center", "className": "", "x": 90, "y": 2040, "wires": [] }, { "id": "56bc3b93af2ebe16", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_acc", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 29, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.1", "max": "2", "step": "0.1", "className": "", "x": 410, "y": 2120, "wires": [ [ "35422077b53da9bf" ] ] }, { "id": "6ef996f8a36f94c2", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_accramp", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 31, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "500", "step": "1", "className": "", "x": 430, "y": 2160, "wires": [ [ "2c000bd53cdb98ca" ] ] }, { "id": "0c50fdbb5ac3c373", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "tt_stepsperrotation", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 25, "width": 3, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 450, "y": 2040, "wires": [ [ "485a4bed5a6bea23" ] ] }, { "id": "213ccfb441a42890", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 30, "width": 3, "height": 1, "name": "ttAccramp", "label": "Acceleration ramp", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 2160, "wires": [] }, { "id": "73c9b4d09dc25e54", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 24, "width": 3, "height": 1, "name": "tt_steps per Rotation", "label": "Steps per Rotation", "format": "", "layout": "row-spread", "className": "", "x": 800, "y": 2040, "wires": [] }, { "id": "a81824c92f22487d", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 28, "width": 3, "height": 1, "name": "tt Acc", "label": "Acceleration", "format": "", "layout": "row-left", "className": "", "x": 750, "y": 2120, "wires": [] }, { "id": "9715161858f69649", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 26, "width": 3, "height": 1, "name": "tt_delay", "label": "Delay", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 2080, "wires": [] }, { "id": "1b3ac50d2c6600c6", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_angle", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 20, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "180", "step": "1", "className": "", "x": 430, "y": 1960, "wires": [ [ "e0d7c36daa42b3f3" ] ] }, { "id": "6dcd1f0ccb01a299", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 19, "width": 3, "height": 1, "name": "rotor_angle", "label": "Manual angle", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 1960, "wires": [] }, { "id": "16e9a3a71c4bb916", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_angle", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 33, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "180", "step": "1", "className": "", "x": 420, "y": 2200, "wires": [ [ "c34111aaec734dd9" ] ] }, { "id": "888161059eb9c71c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 32, "width": 3, "height": 1, "name": "tt_angle", "label": "Manual angle", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2200, "wires": [] }, { "id": "f4fc72297074c7ae", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 4, "width": 6, "height": 1, "name": "rotor", "label": "ROTOR", "format": "", "layout": "row-center", "className": "", "x": 90, "y": 1680, "wires": [] }, { "id": "9b1d8f9e21b34102", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_dir", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 35, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "1", "className": "", "x": 410, "y": 2240, "wires": [ [ "89dbbe7d99ddbbaf" ] ] }, { "id": "b2e839fe47a32b5f", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_dir", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 22, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "1", "className": "", "x": 420, "y": 2000, "wires": [ [ "204b0a5c8629d78a" ] ] }, { "id": "4519daf0b4b28aef", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 34, "width": 3, "height": 1, "name": "tt_dir", "label": "Direction", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2240, "wires": [] }, { "id": "5f269ea2c8a53f6c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 21, "width": 3, "height": 1, "name": "rotor_dir", "label": "Direction", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2000, "wires": [] }, { "id": "b67dfacfc9a23aa5", "type": "link in", "z": "017bd4e4a428bee5", "name": "advanced settings", "links": [ "62cd775a1c02dac8" ], "x": 95, "y": 80, "wires": [ [ "9b756a1f9b0e7317" ] ] }, { "id": "62cd775a1c02dac8", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "b67dfacfc9a23aa5" ], "x": 955, "y": 660, "wires": [] }, { "id": "9d94dbc523d989a3", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_delay_after", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 16, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 450, "y": 2460, "wires": [ [ "b81e238ccd0a04fe" ] ] }, { "id": "0558d6eb9a01862e", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_delay_before", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 14, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 440, "y": 2500, "wires": [ [ "a0048747e7300bdc" ] ] }, { "id": "d47515c9b208bfb7", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_timeout", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 12, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "1", "step": "0.01", "className": "", "x": 420, "y": 2420, "wires": [ [ "9b0d5c521a7822cc" ] ] }, { "id": "89c76766c7552b57", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_gain", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 22, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "10", "step": "0.1", "className": "", "x": 410, "y": 2540, "wires": [ [ "9b26ed02296d27c9" ] ] }, { "id": "c385518eb65a1b27", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_awbg_red", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 18, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-10", "max": "10", "step": "0.1", "className": "", "x": 430, "y": 2580, "wires": [ [ "b0ac7e9a7c713b84" ] ] }, { "id": "5c80833b718d9bf6", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_awbg_blue", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 20, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-10", "max": "10", "step": "0.1", "className": "", "x": 430, "y": 2620, "wires": [ [ "827b1a671a77037d" ] ] }, { "id": "5a3826e112fb24e6", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_contrast", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 24, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 430, "y": 2660, "wires": [ [ "78a1536c167da741" ] ] }, { "id": "3182ed7ac02b1509", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_saturation", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 26, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 430, "y": 2700, "wires": [ [ "fe9a5b68fc8c2077" ] ] }, { "id": "7fa6337cdf0a0bc8", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_jpeg_q", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 3, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "100", "step": "1", "className": "", "x": 420, "y": 2740, "wires": [ [ "e27d2613e942f344" ] ] }, { "id": "08275bf96f87b8ef", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 11, "width": 3, "height": 1, "name": "timeout", "label": "Timeout", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2420, "wires": [] }, { "id": "d2d028df4a139f41", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 15, "width": 3, "height": 1, "name": "delay_after", "label": "Delay after", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2460, "wires": [] }, { "id": "c6a65762aa4ffb7b", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 13, "width": 3, "height": 1, "name": "delay_before", "label": "Delay before", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2500, "wires": [] }, { "id": "780323fd4504b855", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 21, "width": 3, "height": 1, "name": "gain", "label": "Gain", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2540, "wires": [] }, { "id": "780bf08b41202135", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 17, "width": 3, "height": 1, "name": "awbg red", "label": "AWBG red", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2580, "wires": [] }, { "id": "c0faf441fc918538", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 19, "width": 3, "height": 1, "name": "awbg blue", "label": "AWBG blue", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2620, "wires": [] }, { "id": "93d12b447a39c5bb", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 23, "width": 3, "height": 1, "name": "contrast", "label": "Contrast", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2660, "wires": [] }, { "id": "e77e6dcd285d3062", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 25, "width": 3, "height": 1, "name": "saturation", "label": "Saturation", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2700, "wires": [] }, { "id": "a7075bc8d5ee1138", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 2, "width": 3, "height": 1, "name": "jpegQ", "label": "Jpeg Quality", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2740, "wires": [] }, { "id": "282681e7c4351f74", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "ext", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 3, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 2900, "wires": [ [ "b17e82651407d8e0" ] ] }, { "id": "da43c58979737fec", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 2, "width": 4, "height": 1, "name": "ext", "label": "External Camera", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2900, "wires": [] }, { "id": "ef70d61678fe1f11", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "light1", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 5, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 2940, "wires": [ [ "2c812acffdb330c5" ] ] }, { "id": "fec56a7e913b21d6", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 4, "width": 4, "height": 1, "name": "light1", "label": "Light 1", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2940, "wires": [] }, { "id": "24929b4629f22070", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "light2", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 7, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 2980, "wires": [ [ "ae0654af69446942" ] ] }, { "id": "7c6bdc0504aa4cc7", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 6, "width": 4, "height": 1, "name": "light2", "label": "Light 2", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2980, "wires": [] }, { "id": "8c396b060f3d2646", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotordir", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 9, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3020, "wires": [ [ "58cf48cfacc979fb" ] ] }, { "id": "97568610daccf74a", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 8, "width": 4, "height": 1, "name": "rotordir", "label": "Rotor direction", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3020, "wires": [] }, { "id": "a3c58ea48c388215", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotorstep", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 11, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3060, "wires": [ [ "c7ae206f2fff6810" ] ] }, { "id": "6da92aeaeffd95e0", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 10, "width": 4, "height": 1, "name": "rotorstep", "label": "Rotor step", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3060, "wires": [] }, { "id": "9b5da90eaf6ac562", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotoren", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 13, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3100, "wires": [ [ "cfebd4a47a68b319" ] ] }, { "id": "12623e4addfa2c22", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 12, "width": 4, "height": 1, "name": "rotoren", "label": "Rotor enable", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3100, "wires": [] }, { "id": "f24cb404d7d09f8a", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "ttdir", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 15, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 3140, "wires": [ [ "90f4d220928e4727" ] ] }, { "id": "542bfb9d92935c2c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 14, "width": 4, "height": 1, "name": "ttdir", "label": "Turntable direction", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 3140, "wires": [] }, { "id": "1f79467df98ce894", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "ttstep", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 17, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 3180, "wires": [ [ "b05e1e612887f9c2" ] ] }, { "id": "170d3b925f7745cc", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 16, "width": 4, "height": 1, "name": "ttstep", "label": "Turntable step", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 3180, "wires": [] }, { "id": "661614f5bd2c71d6", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "endstop1", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 21, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3220, "wires": [ [ "2af447a6905b83bc" ] ] }, { "id": "c18b55859dae5f85", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 20, "width": 4, "height": 1, "name": "endstop1", "label": "Endstop Rotor", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3220, "wires": [] }, { "id": "e23a396162026618", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "endstop2", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 23, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3260, "wires": [ [ "787a128f84f747c0" ] ] }, { "id": "82c1a33014d003e9", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 22, "width": 4, "height": 1, "name": "endstop2", "label": "Endstop Turntable", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3260, "wires": [] }, { "id": "5255759a7c5b2a74", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "confirm", "x": 680, "y": 660, "wires": [ [ "15fd1c9e5610cb85" ] ] }, { "id": "8be8015931c663cc", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "from OpenScan import save, load_bool\n\nif msg['payload'] == True and not load_bool('advanced_settings'):\n msg['payload'] = '''

PLEASE READ :)

\n

Modifying the advanced settings can potentially damage your device and/or the connected peripherals.

\n

Please read the given information texts carefully and only change settings, when you are sure about the consequences!

\n'''\n return msg\nelif not msg['payload']: \n save('advanced_settings', False)\n", "outputs": 1, "x": 530, "y": 660, "wires": [ [ "5255759a7c5b2a74" ] ] }, { "id": "9d464b2ba1edaf48", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_rotation", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 10, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "270", "step": "90", "className": "", "x": 420, "y": 2780, "wires": [ [ "b7d3fe0c0b40b3e1" ] ] }, { "id": "db98b95693ebce63", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 9, "width": 3, "height": 1, "name": "cam_rot", "label": "Image Rotation", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2780, "wires": [] }, { "id": "6659121906897a1f", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1800, "wires": [ [ "1d4230b3d9b93f63" ] ] }, { "id": "0bb56b1edb12c2cf", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1800, "wires": [ [] ] }, { "id": "569829eeff715c33", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1920, "wires": [ [ "1ab34b0a78b2c577" ] ] }, { "id": "249f44c3a87793ba", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1920, "wires": [ [] ] }, { "id": "c997e60519341afd", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1960, "wires": [ [ "1b3ac50d2c6600c6" ] ] }, { "id": "e0d7c36daa42b3f3", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1960, "wires": [ [] ] }, { "id": "59ecf3a22cd3a669", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2000, "wires": [ [ "b2e839fe47a32b5f" ] ] }, { "id": "204b0a5c8629d78a", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nif (msg.payload === 1){\n content = '1'\n}\nelse{\n content = '-1'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2000, "wires": [ [] ] }, { "id": "15f02421b30a9ab6", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'rotor_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) * 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1840, "wires": [ [ "8c1a92f2dcc976c7" ] ] }, { "id": "bb54bbdae6690576", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload / 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1840, "wires": [ [] ] }, { "id": "58928befcc61b1f7", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'rotor_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1880, "wires": [ [ "f9b51424edb0491c" ] ] }, { "id": "ea87ecfd2af3cc7f", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1880, "wires": [ [] ] }, { "id": "27bc56f273360ac7", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nsteps = parseInt(data);\nif (steps == 3600){\n steps = 3200\n}\n\nmsg.payload = steps\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2040, "wires": [ [ "0c50fdbb5ac3c373" ] ] }, { "id": "f46ced86106306c8", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2160, "wires": [ [ "6ef996f8a36f94c2" ] ] }, { "id": "4339704cd8552eb3", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2200, "wires": [ [ "16e9a3a71c4bb916" ] ] }, { "id": "1ac53bb6150645fe", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2240, "wires": [ [ "9b1d8f9e21b34102" ] ] }, { "id": "9b89eb1eaf333c10", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'tt_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) * 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2080, "wires": [ [ "2647111c06f2055d" ] ] }, { "id": "2e8927be0e235fa1", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'tt_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2120, "wires": [ [ "56bc3b93af2ebe16" ] ] }, { "id": "485a4bed5a6bea23", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2040, "wires": [ [] ] }, { "id": "2c000bd53cdb98ca", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2160, "wires": [ [] ] }, { "id": "c34111aaec734dd9", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2200, "wires": [ [] ] }, { "id": "89dbbe7d99ddbbaf", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nif (msg.payload === 1){\n content = '1'\n}\nelse{\n content = '-1'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2240, "wires": [ [] ] }, { "id": "fb8145a9f8d4f7b2", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload / 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2080, "wires": [ [] ] }, { "id": "35422077b53da9bf", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2120, "wires": [ [] ] }, { "id": "d5308090f2b7971a", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_timeout'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) / 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2420, "wires": [ [ "d47515c9b208bfb7" ] ] }, { "id": "9b0d5c521a7822cc", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_timeout'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload * 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2420, "wires": [ [] ] }, { "id": "694d1068bea15171", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_delay_after'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2460, "wires": [ [ "9d94dbc523d989a3" ] ] }, { "id": "cec3e5e78a40476b", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_delay_before'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2500, "wires": [ [ "0558d6eb9a01862e" ] ] }, { "id": "b81e238ccd0a04fe", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_delay_after'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2460, "wires": [ [] ] }, { "id": "a0048747e7300bdc", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_delay_before'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2500, "wires": [ [] ] }, { "id": "6f524f9370a18482", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_gain'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2540, "wires": [ [ "89c76766c7552b57" ] ] }, { "id": "9b26ed02296d27c9", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_gain'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2540, "wires": [ [] ] }, { "id": "1f87f473e327c3cc", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_awbg_red'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2580, "wires": [ [ "c385518eb65a1b27" ] ] }, { "id": "b0ac7e9a7c713b84", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_awbg_red'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2580, "wires": [ [] ] }, { "id": "cff7ac5f1e061855", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_awbg_blue'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2620, "wires": [ [ "5c80833b718d9bf6" ] ] }, { "id": "827b1a671a77037d", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_awbg_blue'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2620, "wires": [ [] ] }, { "id": "cf854461c37ca54f", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_contrast'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2660, "wires": [ [ "5a3826e112fb24e6" ] ] }, { "id": "78a1536c167da741", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_contrast'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2660, "wires": [ [] ] }, { "id": "ba10e04dd1761692", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_saturation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2700, "wires": [ [ "3182ed7ac02b1509" ] ] }, { "id": "fe9a5b68fc8c2077", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_saturation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2700, "wires": [ [] ] }, { "id": "a69d216114f908a5", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_jpeg_quality'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2740, "wires": [ [ "7fa6337cdf0a0bc8" ] ] }, { "id": "e27d2613e942f344", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_jpeg_quality'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2740, "wires": [ [] ] }, { "id": "f02d4a036a225e87", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_rotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2780, "wires": [ [ "9d464b2ba1edaf48" ] ] }, { "id": "b7d3fe0c0b40b3e1", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_rotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2780, "wires": [ [] ] }, { "id": "612cccacda1a65aa", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_external'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2900, "wires": [ [ "282681e7c4351f74" ] ] }, { "id": "b17e82651407d8e0", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_external'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 2900, "wires": [ [] ] }, { "id": "3b126549c03a872e", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_rotor_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3220, "wires": [ [ "661614f5bd2c71d6" ] ] }, { "id": "2af447a6905b83bc", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_rotor_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3220, "wires": [ [] ] }, { "id": "954db931f87894ee", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_ringlight1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2940, "wires": [ [ "ef70d61678fe1f11" ] ] }, { "id": "2c812acffdb330c5", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_ringlight1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 2940, "wires": [ [] ] }, { "id": "6682c8057e89d087", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_ringlight2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2980, "wires": [ [ "24929b4629f22070" ] ] }, { "id": "ae0654af69446942", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_ringlight2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 2980, "wires": [ [] ] }, { "id": "015be401d08047d2", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3020, "wires": [ [ "8c396b060f3d2646" ] ] }, { "id": "58cf48cfacc979fb", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3020, "wires": [ [] ] }, { "id": "1c6c0f8b9ac95659", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_rotor_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3060, "wires": [ [ "a3c58ea48c388215" ] ] }, { "id": "c7ae206f2fff6810", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_rotor_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3060, "wires": [ [] ] }, { "id": "dcee66c0d56c6934", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_rotor_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3100, "wires": [ [ "9b5da90eaf6ac562" ] ] }, { "id": "cfebd4a47a68b319", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_rotor_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3100, "wires": [ [] ] }, { "id": "6ec7d85bb17eb159", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3140, "wires": [ [ "f24cb404d7d09f8a" ] ] }, { "id": "4f42d02a3776a006", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_tt_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3180, "wires": [ [ "1f79467df98ce894" ] ] }, { "id": "90f4d220928e4727", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3140, "wires": [ [] ] }, { "id": "b05e1e612887f9c2", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_tt_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3180, "wires": [ [] ] }, { "id": "58bbe9fc41e0d7b9", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_tt_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3260, "wires": [ [ "e23a396162026618" ] ] }, { "id": "787a128f84f747c0", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_tt_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3260, "wires": [ [] ] }, { "id": "78351089ee9ebeaf", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'ssh'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 340, "wires": [ [ "40dee936a9abac0d" ] ] }, { "id": "5fba78ae65eaaf5d", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'smb'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 380, "wires": [ [ "4fd9bb53fdb51a25" ] ] }, { "id": "67206663b3881868", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'advanced_settings'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 660, "wires": [ [ "c833f6243a059d83" ] ] }, { "id": "3492754252645e62", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'camera'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data.replace(/(\\r\\n|\\n|\\r)/gm,\"\"));\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 420, "wires": [ [ "a2c1dba3e67be015", "6f3d403e157163e4" ] ] }, { "id": "d16525a31223bc42", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'model'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 620, "wires": [ [ "80b579a4220e5c23", "c6138801b30f091d" ] ] }, { "id": "f99ec8781a33ec7d", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.payload = 'This is a free piece of software and it is provided as is, without any warranty.
There might be functions that need a connection to the internet: '+\n '

By pressing GET FEATURES you agree that the shown preview image will be transfered, stored and processed via SFTP to my servers '+\n '(Thomas Megel, OpenScan, Halle, Germany). The IP address will be saved for 14 days The images might be used for further experiments (e.g. machine learning, automation ...). '+\n '

By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\n 'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\n 'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\n '

By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\n 'The raw images and resulting 3d models will never be published without your explicit consent.'+ \n '

If you have any questions you can contact me at info@openscan.eu.'+ \n '

THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\n 'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \n 'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \n 'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 300, "wires": [ [ "7dc39bd847d16ded" ] ] }, { "id": "5f849178998d9082", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "if(msg.payload === 'Agree'){\n data = true;\n}\nelse{\n data = false;\n}\nvar file = 'terms'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nfs.writeFile(filepath+file, String(data), err => {\n if (err) {\n return msg\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 550, "y": 300, "wires": [ [] ] }, { "id": "725fd0cab0bddc0e", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'hostname'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 940, "wires": [ [ "49259adad52fc214" ] ] }, { "id": "49259adad52fc214", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "", "label": "Hostname", "tooltip": "", "group": "0fe66c9190b8a87c", "order": 6, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "Change hostname to:", "sendOnBlur": true, "className": "", "topicType": "str", "x": 530, "y": 940, "wires": [ [ "8001f7c361de7d8c" ] ] }, { "id": "51521bc6eb44cde5", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 980, "wires": [ [ "59c9f67283ba1709" ] ] }, { "id": "2bb52656f9554dab", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "ssid = msg.payload\nmsg.topic = 'Add wifi network (' + ssid + ')'\nmsg.payload = 'Enter Wifi password:'\nmsg.ssid = ssid\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 650, "y": 980, "wires": [ [ "ebcc98685059b9d4" ] ] }, { "id": "ebce67b739d1891f", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "chk/change hostname", "func": "from OpenScan import save\n\nif msg['payload'] != 'OK':\n pass\n\nwith open('/etc/hostname', 'r') as file:\n old_hostname = file.read().replace('\\n','')\n\nhostname = msg['hostname']\nif len(hostname) < 4 :\n msg['payload'] = ' '\n msg['topic'] = 'ERROR - Hostname NOT changed'\n return msg\n \n\nwith open('/etc/hostname', 'w+') as file:\n file.write(hostname)\nos.system('echo ' + hostname + ' | tee /etc/hostname')\nwith open('/etc/hosts', 'r') as file:\n temp = file.read()\ntemp = temp.replace(old_hostname,hostname)\nwith open('/etc/hosts', 'w') as file:\n file.write(temp)\nos.system('hostnamectl set-hostname ' + hostname)\nos.system('systemctl restart avahi-daemon')\nsave('hostname',hostname)\nmsg['payload'] = hostname\nmsg['topic'] = 'Success - Hostname changed'\nreturn msg\n", "outputs": 1, "x": 1140, "y": 940, "wires": [ [ "03732a7d3b0c95aa" ] ] }, { "id": "667ac2aba819f506", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "Confirm", "x": 920, "y": 940, "wires": [ [ "ebce67b739d1891f" ] ] }, { "id": "8001f7c361de7d8c", "type": "change", "z": "017bd4e4a428bee5", "name": "", "rules": [ { "t": "set", "p": "hostname", "pt": "msg", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 710, "y": 940, "wires": [ [ "667ac2aba819f506" ] ] }, { "id": "9bb0adbd716ce347", "type": "link in", "z": "017bd4e4a428bee5", "name": "reboot", "links": [ "16c76929f88df841", "fe3a855fee9e28c6" ], "x": 155, "y": 720, "wires": [ [ "d114f4d4d7f31981", "cc3cb10f2ea3f8b8" ] ] }, { "id": "f9efcb87b74abbd4", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "if (msg.payload === 'Cancel'){\n return\n}\nmsg.forename = msg.payload\nmsg.topic = 'OpenScanCloud Registration (3/3)'\nmsg.payload = 'Enter your last name'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 670, "y": 1520, "wires": [ [ "510dbe4d76253bd6" ] ] }, { "id": "adc206aa8edd1e41", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "OSC", "group": "db43d646.2074c8", "order": 2, "width": 3, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Files&Cloud

Refresh

You can refresh the status of the processing of your files in the OpenScanCloud. Make sure to read and agree the terms of use (in settings menu) before using the OpenScanCloud. Do not spam this button, as this might lead to temporary/permanent suspension of your IP address.

The status (in the table) of the individual sets in the file list will be updated to one of the following:

Created - you started the upload of your image set. If you are stuck on this status, please try to restart the upload.

Initialized - all files have been uploaded and processing will start as soon as possible

File approved - the server received and verified your files

Processing started - your files are currently being processed

Processing failed - there are various reasons why processing might fail. Please check the email for more details or contact me at cloud@openscan.eu

processing done - check your email, where you should find a link to the 3d model :)

Status (on the right column)

Indicates, what the device is currently up to.

Refreshing - updating all image set's status

Uploading - while transferring the image set to the OpenScanCloud servers. If the upload freezes, be patient. If nothing happens, reboot the device and restart the upload.

Project started - when the upload of a set was successful

Zipping - files larger then 200mb have to be split and re-zipped before uploading to the OpenScanCloud, the process might take a while depending on the filesize.

Combining - two sets into one might take up to a minute.

Set

select a set from the file list by clicking on a row in the table

Download

Download the selected set from the OpenScan device to your computer/mobile/tablet

Upload

Upload the selected file to the OpenScanCloud

Combine

In order to combine two sets, select one set. Click the combine button and select the second set. A pop-up will appear, and you can confirm the operation. All images from the two sets will be merged into one set. The original image sets will be deleted!

Delete Set/All

Please keep in mind, that the memory of the SD card is relatively small, and thus you will have to delete individual or all photo sets from time to time.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 590, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "45df91cae421e8e1", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Scan_settings", "group": "7aaf184330605300", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Scan Settings

Current Status

--READY-- - everything is okay and ready to go :)

Routine-preparing - before starting the routine some time might pass depending on the number of photos

Routine-stopping - manually ending the routine by pressing the stop button

Routine-Photo X/Y - Showing the progress of the routine

No Camera Found - please check the camera ribbon cable

Error: XXX - Please contact info@openscan.eu or post an issue on Github.com

Projectname

Each photo set will be saved using the following pattern  YYYY-MM-DD_hh-mm-ss_projectname.zip (e.g. 2022-04-05_12.12.12_toysoldier.zip). Keep your files organized by giving each set a new projectname. If not specified 'default' will be used.

Rotor

Moving the rotor by increments of 5°. Please make sure to start the routine with the camera in the horizontal position.

Turntable

Moving the turntable by increments of 15°.

Ringlight

Use the ring light for shadow-free illumination. It is highly recommended to use the polarizer in order to avoid reflections. Note, that the polarizer will absorb 75% of the light, so you might need to use both ring lights.

Photos

Set the number of photos for the current set. 60-120 photos should be more than enough for most objects. If the reconstruction fails or is very bad with 60 photos, increasing the number of photos will not help!

Shutter

Again: Less is more! If the value is too high, some areas might get overexposed and thus, the software will not be able to recognize the surface feature of the object. Here are some reference values:

- no polarizer: 5-20ms

- mostly white object,  with polarizer + one ringlight: 50-200ms

Crop X/Y

Make sure to use the right object holder to place the object in the middle of the screen. Try to crop as many unnecessary areas as possible. This will greatly lower the file size and resulting transfer and reconstruction times!

Start/Stop

Use the buttons to start/stop the routine

Reboot/Shutdown

In case of an error, try to restart the device. Always use the shutdown button before powering-off the device!

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 760, "y": 120, "wires": [ [ "f304680180a23479" ] ] }, { "id": "e9677b85856b5873", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Reset rfkill", "func": "from os import system\nif \"Interface doesn't support scanning\" in msg['payload']:\n system('rfkill unblock all')\n system('ifconfig wlan0 up')\n return msg", "outputs": 1, "x": 390, "y": 1100, "wires": [ [] ] }, { "id": "91fe20cb16f54293", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_anglemin'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1680, "wires": [ [ "327c8bdde31033a4" ] ] }, { "id": "add3e998b097c54f", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 7, "width": 3, "height": 1, "name": "rotor_anglemin", "label": "Min Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1680, "wires": [] }, { "id": "da286366433c83a0", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_anglemin'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1680, "wires": [ [] ] }, { "id": "327c8bdde31033a4", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_anglemin", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 8, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1680, "wires": [ [ "da286366433c83a0" ] ] }, { "id": "94288df4c6756197", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_anglemax", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 10, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1720, "wires": [ [ "e531ffe3dcf34eb4" ] ] }, { "id": "4702a4a09124e27d", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_anglestart", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 6, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1760, "wires": [ [ "9ce407cb16f0419a" ] ] }, { "id": "2cf946c7aab2cbb4", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_anglemax'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1720, "wires": [ [ "94288df4c6756197" ] ] }, { "id": "e531ffe3dcf34eb4", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_anglemax'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1720, "wires": [ [] ] }, { "id": "4da5f650d3845baa", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_anglestart'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1760, "wires": [ [ "4702a4a09124e27d" ] ] }, { "id": "9ce407cb16f0419a", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_anglestart'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1760, "wires": [ [] ] }, { "id": "fda776c5aa642867", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 9, "width": 3, "height": 1, "name": "rotor_anglemax", "label": "Max Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1720, "wires": [] }, { "id": "6e9af48a1c4c58c6", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 5, "width": 3, "height": 1, "name": "rotor_anglestart", "label": "Start Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1760, "wires": [] }, { "id": "9b2bc9849aee310b", "type": "link in", "z": "017bd4e4a428bee5", "name": "changeHostname", "links": [ "ec2db55a99bbe3ee", "d5175561293ef490", "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 835, "y": 900, "wires": [ [ "8b9e3781511e9231" ] ] }, { "id": "8b9e3781511e9231", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "chk", "func": "with open('/etc/hostname', 'r') as file:\n old_hostname = file.read().replace('\\n','')\nif old_hostname == 'raspberrypi':\n msg['hostname'] = 'openscan'\n msg['payload'] = 'OK'\n return msg", "outputs": 1, "x": 930, "y": 900, "wires": [ [ "ebce67b739d1891f" ] ] }, { "id": "3fcbd9fe3acc3fb7", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "scan_arducam", "group": "90223f7ddc082321", "order": 1, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Focus Settings

MF - Manual Focus

By default, the switch is 'off', which means that autofocus is active. For small objects, it might be necessary to use manual focus: activate the switch and set the focus by pressing + and - accordingly. The distance is measured between the camera lens and the focal plane (which should be in the center or slightly in front of the center of the object). Be aware, that the distance value is only a rough estimate (mm)

ST - Stacking

Stacking is disabled by default. Once activated, you will be able to set the following:

Stacksize - defines the number of photos between the minimal and the maximal focal distance

SET press this button to set the maximal/minimal focal distance. Pressing the button a third time will re-set the values.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 760, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "6d68cccec646e0a0", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "enable routine", "func": "msg_enable = {}\nmsg_disable = {}\n\nmsg_enable['enabled'] = True\nmsg_disable['enabled'] = False\n\nif msg['payload'] == 'external':\n return msg_enable, msg_disable\nif msg['payload'] == 'gphoto':\n return msg_enable, msg_enable, msg_disable\n\nreturn msg_enable", "outputs": 3, "x": 560, "y": 440, "wires": [ [ "a0ba1aa77c5c8b7c" ], [ "a42c12e94f65fa01" ], [ "2d76e5617f13cd6c" ] ] }, { "id": "a0ba1aa77c5c8b7c", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "2aea1727dbea76ce", "4f212b44aa487945", "65cef204b16f8741", "917a194be245384a" ], "x": 675, "y": 420, "wires": [] }, { "id": "a42c12e94f65fa01", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "2aea1727dbea76ce", "4f212b44aa487945", "65cef204b16f8741", "917a194be245384a" ], "x": 715, "y": 440, "wires": [] }, { "id": "2d76e5617f13cd6c", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "65cef204b16f8741" ], "x": 675, "y": 460, "wires": [] }, { "id": "bd80ec228fb9a86d", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 135, "y": 340, "wires": [ [ "78351089ee9ebeaf", "5fba78ae65eaaf5d", "3492754252645e62", "d16525a31223bc42", "67206663b3881868" ] ] }, { "id": "65b38bfeb3fee710", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 155, "y": 760, "wires": [ [ "cc3cb10f2ea3f8b8" ] ] }, { "id": "d3fc91d87d5d5f62", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 135, "y": 940, "wires": [ [ "725fd0cab0bddc0e" ] ] }, { "id": "cc9c4092edeb43cc", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 135, "y": 1020, "wires": [ [ "27c6b221c90ed9e1", "f393400.d87dcc" ] ] }, { "id": "f0b355967b33dfee", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 175, "y": 1600, "wires": [ [ "91fe20cb16f54293", "2cf946c7aab2cbb4", "4da5f650d3845baa", "6659121906897a1f", "15f02421b30a9ab6", "58928befcc61b1f7", "569829eeff715c33", "c997e60519341afd", "59ecf3a22cd3a669", "27bc56f273360ac7", "9b89eb1eaf333c10", "2e8927be0e235fa1", "f46ced86106306c8", "4339704cd8552eb3", "1ac53bb6150645fe", "0d48bb415c584420", "b6e420121e6466e7" ] ] }, { "id": "d7c1fb4c028b21a5", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 155, "y": 2280, "wires": [ [ "d5308090f2b7971a", "694d1068bea15171", "cec3e5e78a40476b", "6f524f9370a18482", "1f87f473e327c3cc", "cff7ac5f1e061855", "cf854461c37ca54f", "ba10e04dd1761692", "a69d216114f908a5", "f02d4a036a225e87", "1efd4a05aee0b86c", "6841e5a392f0fb4f" ] ] }, { "id": "a67c18aaca2f5fa5", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 155, "y": 2900, "wires": [ [ "612cccacda1a65aa", "954db931f87894ee", "6682c8057e89d087", "015be401d08047d2", "1c6c0f8b9ac95659", "dcee66c0d56c6934", "6ec7d85bb17eb159", "4f42d02a3776a006", "3b126549c03a872e", "58bbe9fc41e0d7b9" ] ] }, { "id": "c6d3821bc7f43f8e", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Reset default", "group": "4fe6b4c0ade0938a", "order": 14, "width": 6, "height": 1, "passthru": false, "label": "Restore default settings", "tooltip": "", "color": "red", "bgcolor": "", "className": "", "icon": "", "payload": "This can not be undone!", "payloadType": "str", "topic": "Restore default settings?", "topicType": "str", "x": 930, "y": 300, "wires": [ [ "e4be21c38b57f560" ] ] }, { "id": "e4be21c38b57f560", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 1090, "y": 300, "wires": [ [ "9f30de04ced693d3" ] ] }, { "id": "9f30de04ced693d3", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.overwrite = true\nif(msg.payload == \"Yes\"){\n return msg}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1230, "y": 300, "wires": [ [ "80bccc884b0be297" ] ] }, { "id": "80bccc884b0be297", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "38783aea9cc317a6" ], "x": 1325, "y": 300, "wires": [] }, { "id": "34b685aff2080d31", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "boot-cam", "func": "from OpenScan import load_str\n\ncamera_modules = ('imx519', 'imx219', 'ov5647', 'imx477', 'imx378', 'ov9281', 'imx290a', 'imx290b')\n\npt1 = \"[all]\\n\\ncamera_auto_detect=0\\ngpu_mem=256\\ndtoverlay=vc4-fkms-v3d\\ndtoverlay=\"\npt3 = \",media-controller=1\\n\"\n\nwith open('/boot/config.txt', 'r') as file:\n config = file.read()\n\ncamera = load_str('camera')\nif camera not in camera_modules:\n msg['payload'] = 'no changes'\n return\n\nif camera == 'imx290a':\n camera = 'imx290,clock-frequency=37125000'\nelif camera == 'imx290b':\n camera = 'imx290,clock-frequency=74250000'\n\nconfig_keep = config.split('[all]\\n')[0]\nconfig_new = config_keep + pt1 + camera + pt3\n\nwith open('/boot/config.txt', 'w') as file:\n file.write(config_new)\n\nmsg['topic'] = 'Camera configuration changed'\nmsg['payload'] = 'Please restart the device'\n\nreturn msg", "outputs": 1, "x": 680, "y": 500, "wires": [ [ "68cba0c530c6def6" ] ] }, { "id": "68cba0c530c6def6", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 830, "y": 500, "wires": [ [] ] }, { "id": "f304680180a23479", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": true, "className": "", "topic": "", "name": "Info", "x": 1010, "y": 120, "wires": [ [] ] }, { "id": "0d48bb415c584420", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'turntable_mode'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1640, "wires": [ [ "ce215e159ce7267f" ] ] }, { "id": "ce215e159ce7267f", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Turntable Mode", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 2, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 440, "y": 1640, "wires": [ [ "f95f528dec31425c" ] ] }, { "id": "f95f528dec31425c", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'turntable_mode'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1640, "wires": [ [] ] }, { "id": "4ebe5baece5ce9f2", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "preview_resolution", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 5, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.5", "max": "10", "step": "0.5", "className": "", "x": 450, "y": 2280, "wires": [ [ "60a415fff23cb55e" ] ] }, { "id": "9ed0498cceceedde", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 4, "width": 3, "height": 1, "name": "preview_res", "label": "Preview Resolution (Mpx)", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2280, "wires": [] }, { "id": "1efd4a05aee0b86c", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_preview_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data)/1000000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2280, "wires": [ [ "4ebe5baece5ce9f2" ] ] }, { "id": "60a415fff23cb55e", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_preview_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload*1000000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2280, "wires": [ [] ] }, { "id": "6f3d403e157163e4", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "Camera", "label": "", "tooltip": "", "place": "Select option", "group": "1f7f7e1e24f5ad9b", "order": 5, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Pi Cam v1 - 5mp", "value": "ov5647", "type": "str" }, { "label": "Pi Cam v2 - 8mp", "value": "imx219", "type": "str" }, { "label": "Pi Cam HQ - 12.3mp", "value": "imx477", "type": "str" }, { "label": "Arducam IMX519 - 16mp", "value": "imx519", "type": "str" }, { "label": "IMX290 a", "value": "imx290a", "type": "str" }, { "label": "IMX290 b", "value": "imx290b", "type": "str" }, { "label": "IMX378", "value": "imx378", "type": "str" }, { "label": "OV9281", "value": "ov9281", "type": "str" }, { "label": "DSLR (gphoto)", "value": "gphoto", "type": "str" }, { "label": "USB Webcam", "value": "usb_webcam", "type": "str" }, { "label": "External Camera", "value": "external", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 400, "y": 460, "wires": [ [ "6d68cccec646e0a0", "4058a31e942e8f95" ] ] }, { "id": "c6138801b30f091d", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "model", "label": "", "tooltip": "", "place": "Select option", "group": "1f7f7e1e24f5ad9b", "order": 3, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Please Select", "value": "None", "type": "str" }, { "label": "OpenScan Mini", "value": "OSMini", "type": "str" }, { "label": "OpenScan Classic", "value": "OSClassic", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 390, "y": 580, "wires": [ [ "896242c5a7e50fa7" ] ] }, { "id": "4da67c23c7a543a0", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "1f7f7e1e24f5ad9b", "order": 4, "width": 2, "height": 1, "name": "", "label": "Camera", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 840, "y": 460, "wires": [] }, { "id": "1fed8676078ea9a7", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "1f7f7e1e24f5ad9b", "order": 2, "width": 2, "height": 1, "name": "", "label": "Model", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 730, "y": 580, "wires": [] }, { "id": "a4b7eea9a9736b0a", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Update&Info", "group": "ddbd496e.93a288", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Update&Log

Status

See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on Github.com for details and the source code.

Updatetype

- stable: latest well-tested and mostly bug-free version for the OpenScanMini or Classic and various cameras

- beta: stable version + some experimental and new features, which might bring joy and some new bugs as well

- mini: very simplified firmware for the OpenScanMini + Arducam IMX519

Auto-Check update availability

Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on Github.com

This option is activated by default.

Check Updates

Alternatively, you can check for updates manually at any time by pressing this button.

Download Error Log

In case you encounter any errors with your device, please download the error log text and send a copy to info@openscan.eu or create an issue on Github.com

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 750, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "b6e420121e6466e7", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'routine_secondpass'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1600, "wires": [ [ "ab8d5cfe9190bb5f" ] ] }, { "id": "ab8d5cfe9190bb5f", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Second pass", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 3, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 430, "y": 1600, "wires": [ [ "fa51327f0140b045" ] ] }, { "id": "fa51327f0140b045", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'routine_secondpass'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1600, "wires": [ [] ] }, { "id": "6841e5a392f0fb4f", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'cam_output_downscale'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2320, "wires": [ [ "110216d678fad14f" ] ] }, { "id": "110216d678fad14f", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Downscale output", "tooltip": "", "group": "93aadb71dee6d977", "order": 6, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 450, "y": 2320, "wires": [ [ "214d548d564f8ba2" ] ] }, { "id": "214d548d564f8ba2", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_output_downscale'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \nmsg.enabled = msg.payload\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2320, "wires": [ [ "1becbff4884b8c1a" ] ] }, { "id": "8be1ca844a6caa54", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "output_resolution", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 8, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.5", "max": "20", "step": "0.5", "className": "", "x": 450, "y": 2360, "wires": [ [ "a6b2c0a0604ccf14" ] ] }, { "id": "9ac09d89d791e953", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 7, "width": 3, "height": 1, "name": "image_res", "label": "Output Resolution (Mpx)", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2360, "wires": [] }, { "id": "1becbff4884b8c1a", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_output_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data)/1000000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2360, "wires": [ [ "8be1ca844a6caa54" ] ] }, { "id": "a6b2c0a0604ccf14", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_output_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload*1000000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2360, "wires": [ [] ] }, { "id": "f358de1e64b491bb", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "b30d918661392ab3", "44c598049cd533fd" ], "x": 635, "y": 620, "wires": [] }, { "id": "b0629875a30ae1d7", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "get update", "func": "import json\nimport requests\nfrom OpenScan import load_str\n\nif not msg['payload']:\n msg['status'] = '--READY--'\n return msg\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'no internet connection'\n return msg\n\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg['status'] = 'checking updates'\n\nreturn msg, msg", "outputs": 2, "x": 350, "y": 240, "wires": [ [ "1bbe2d769f42c313" ], [ "fefe45404bdb19c4" ] ] }, { "id": "c7b6d05a62172432", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "ddbd496e.93a288", "order": 3, "width": 0, "height": 0, "name": "", "label": "Status:", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 170, "y": 100, "wires": [] }, { "id": "fefe45404bdb19c4", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "check files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str\n\nscope = load_str('update_type')\nmsg['scope'] = scope\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_old = updatepath + 'update.json'\n\ncounter = 0\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n if os.path.isfile(temp):\n filesize = os.path.getsize(temp)\n msg[scope][i]['filesize2'] = filesize\n if filesize == msg[scope][i]['filesize']:\n msg[scope][i]['update'] = False\n continue\n msg[scope][i]['update'] = True\n\n counter += 1\n\nif counter == 0:\n msg['status'] = 'No new update available'\nelse:\n msg['status'] = 'New update available'\n msg['topic'] = msg['status']\n msg['payload'] = 'Install & reboot now?'\n\nmsg['counter'] = counter\n\nreturn msg\n", "outputs": 1, "x": 510, "y": 260, "wires": [ [ "1bbe2d769f42c313", "ae92a328af306ebb" ] ] }, { "id": "d0104e0163745993", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 75, "y": 140, "wires": [ [ "ec30638407332e43", "38cbf7965d1c1834", "49f1ecb29a3f84f4" ] ] }, { "id": "ec30638407332e43", "type": "function", "z": "c8e7ecb5849edb9a", "name": "loadS", "func": "var file = 'update_type'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data.replace(/(\\r\\n|\\n|\\r)/gm,\"\"));\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 180, "wires": [ [ "2852023f3aa8db10" ] ] }, { "id": "2852023f3aa8db10", "type": "ui_dropdown", "z": "c8e7ecb5849edb9a", "name": "", "label": "", "tooltip": "", "place": "Select option", "group": "ddbd496e.93a288", "order": 5, "width": 2, "height": 1, "passthru": false, "multiple": false, "options": [ { "label": "stable", "value": "main", "type": "str" }, { "label": "beta", "value": "beta", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 300, "y": 180, "wires": [ [ "1e10b387ee30c486" ] ] }, { "id": "1e10b387ee30c486", "type": "function", "z": "c8e7ecb5849edb9a", "name": "write", "func": "var file = 'update_type'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 180, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "274129c51b0b87ef", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "ddbd496e.93a288", "order": 4, "width": 4, "height": 1, "name": "", "label": "Updatetype: ", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 570, "y": 180, "wires": [] }, { "id": "51cd8c8643e6b46a", "type": "ui_switch", "z": "c8e7ecb5849edb9a", "name": "", "label": "Auto-check update availability", "tooltip": "", "group": "ddbd496e.93a288", "order": 6, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 370, "y": 140, "wires": [ [ "1ab4c6b4b232a022" ] ] }, { "id": "38cbf7965d1c1834", "type": "function", "z": "c8e7ecb5849edb9a", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 140, "wires": [ [ "51cd8c8643e6b46a" ] ] }, { "id": "1ab4c6b4b232a022", "type": "function", "z": "c8e7ecb5849edb9a", "name": "write", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 570, "y": 140, "wires": [ [] ] }, { "id": "ae92a328af306ebb", "type": "ui_toast", "z": "c8e7ecb5849edb9a", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "NO", "cancel": "YES", "raw": false, "className": "", "topic": "", "name": "", "x": 670, "y": 260, "wires": [ [ "2de63e8e3ae5fb0c", "929281fef53e09f8" ] ] }, { "id": "cbd0afc4aa7b302a", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "update status", "links": [ "1bbe2d769f42c313", "42061b28cff81f99" ], "x": 75, "y": 100, "wires": [ [ "c7b6d05a62172432", "c94623ddd9d95f78" ] ] }, { "id": "1bbe2d769f42c313", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "cbd0afc4aa7b302a" ], "x": 625, "y": 220, "wires": [] }, { "id": "7cf60615d93e696b", "type": "ui_button", "z": "c8e7ecb5849edb9a", "name": "", "group": "ddbd496e.93a288", "order": 7, "width": 6, "height": 1, "passthru": false, "label": "Check Updates", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 140, "y": 260, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "2de63e8e3ae5fb0c", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "download files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str, save\n\n\nscope = msg['scope']\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\n\nif msg['payload'] != 'YES':\n return\n\ncounter = 0\n\nfor i in msg[scope]:\n if msg[scope][i]['update'] == False:\n continue\n \n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n \n r = requests.get(url + msg[scope][i]['src'])\n if r.status_code != 200:\n msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\n return msg\n with open(temp, 'wb+') as file:\n file.write(r.content)\n shutil.copy(temp, msg[scope][i]['dst'])\n \n if msg[scope][i]['dst'] == '/boot/config.txt':\n save('camera','')\n \n counter += 1\n\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\n\nif counter == msg['counter']:\n updatepath_temp = updatepath + 'update_temp.json'\n updatepath_old = updatepath + 'update.json'\n shutil.move(updatepath_temp, updatepath_old)\n\nreturn msg\n", "outputs": 1, "x": 840, "y": 260, "wires": [ [ "42061b28cff81f99", "fe3a855fee9e28c6" ] ] }, { "id": "929281fef53e09f8", "type": "function", "z": "c8e7ecb5849edb9a", "name": "msg", "func": "if (msg.payload == 'YES'){\n msg.status = 'Installing updates'\n return msg}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 220, "wires": [ [ "42061b28cff81f99" ] ] }, { "id": "42061b28cff81f99", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "cbd0afc4aa7b302a" ], "x": 955, "y": 220, "wires": [] }, { "id": "49f1ecb29a3f84f4", "type": "function", "z": "c8e7ecb5849edb9a", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\n\nmsg.payload = data\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 220, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "fe3a855fee9e28c6", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "9bb0adbd716ce347" ], "x": 955, "y": 260, "wires": [] }, { "id": "5e7d5e4335d37794", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 55, "y": 400, "wires": [ [ "2bb5fe78e09fec8a" ] ] }, { "id": "2bb5fe78e09fec8a", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "msg", "func": "\nfrom subprocess import getoutput\nimport os\n\nmsg['os'] = getoutput(\"cat /etc/os-release | grep -i 'PRETTY_NAME'\")[13:-1]\nmsg['device'] = getoutput(\"cat /proc/device-tree/model\")\nmsg['flask'] = getoutput(\"systemctl status flask |grep -i 'Active:'\").split(' ')[6]\nmsg['osdate'] = getoutput(\"vcgencmd version\").split('\\n')[0]\nmsg['temp'] = getoutput(\"vcgencmd measure_temp\").split('=')[1]\ncpu_total = getoutput(\"free -m | head -n2 | tail -n1 |awk '{print $2}'\")\ncpu_used = getoutput(\"free -m | head -n2 | tail -n1 |awk '{print $3}'\")\nswap_total = getoutput(\"free -m | head -n3 | tail -n1 |awk '{print $2}'\")\nswap_used = getoutput(\"free -m | head -n3 | tail -n1 |awk '{print $3}'\")\ndiskspace_used = getoutput(\"df -h / | tail -n1 |awk '{print $3}'\")\ndiskspace_total = getoutput(\"df -h / | tail -n1 |awk '{print $2}'\")\n\nmsg['cpu'] = cpu_used + '/' + cpu_total + 'MB'\nmsg['swap'] = swap_used + '/' + swap_total + 'MB'\nmsg['diskspace'] =diskspace_used + '/' + diskspace_total\n\nif msg['flask'] == 'inactive':\n os.system('systemctl restart flask')\n\nreturn msg", "outputs": 1, "x": 170, "y": 400, "wires": [ [ "dbc77052ac950624", "d97c3068ef5fef96", "73a3b828f862312b", "901e31453b2bdff8", "f983854748ee4763", "5347c7c517f5e8c7", "3a5016f7003cd72c", "6d720c4a4ecd9475", "6438b7d060a70d81" ] ] }, { "id": "d97c3068ef5fef96", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 2, "width": 0, "height": 0, "name": "", "label": "OS:", "format": "{{msg.os}}", "layout": "row-spread", "className": "", "x": 450, "y": 440, "wires": [] }, { "id": "73a3b828f862312b", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 8, "width": 0, "height": 0, "name": "", "label": "Flask:", "format": "{{msg.flask}}", "layout": "row-spread", "className": "", "x": 450, "y": 480, "wires": [] }, { "id": "dbc77052ac950624", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 1, "width": 0, "height": 0, "name": "", "label": "Device:", "format": "{{msg.device}}", "layout": "row-spread", "className": "", "x": 460, "y": 400, "wires": [] }, { "id": "4c7fa5b5b27b83a5", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "create beta new", "func": "import json\nimport requests\nimport shutil\n\n#scope = 'main'\nscope = 'beta'\n\nupdatepath = '/home/pi/OpenScan/tmp/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\n\n## load update.json\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'download update.json failed'\n return msg\n\nmsg = {}\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg2 = msg.copy()\ndel msg[scope]\n\nmsg[scope]={}\nmsg[scope]['1'] = {}\nmsg[scope]['1']['src'] = scope + '/fla.py'\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\n\nmsg[scope]['2'] = {}\nmsg[scope]['2']['src'] = scope + '/Arducam.py'\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/Arducam.py'\n\nmsg[scope]['3'] = {}\nmsg[scope]['3']['src'] = scope + '/OpenScan.py'\nmsg[scope]['3']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\n\nmsg[scope]['4'] = {}\nmsg[scope]['4']['src'] = scope + '/config.txt'\nmsg[scope]['4']['dst'] = '/boot/config.txt'\n\nmsg[scope]['5'] = {}\nmsg[scope]['5']['src'] = scope + '/flows.json'\nmsg[scope]['5']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\n\nmsg[scope]['6'] = {}\nmsg[scope]['6']['src'] = scope + '/settings.js'\nmsg[scope]['6']['dst'] = '/root/.node-red/settings.js'\n\nmsg[scope]['7'] = {}\nmsg[scope]['7']['src'] = 'files/logo.jpg'\nmsg[scope]['7']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\n\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n filesize = os.path.getsize(filepath)\n msg[scope][i]['filesize'] = filesize\n\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\n os.system('rm -r /home/pi/OpenScan/tmp/update') \nos.makedirs('/home/pi/OpenScan/tmp/update/')\n\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\n json.dump(msg, f, indent=4)\n\nfor i in msg[scope]:\n if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\n os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\n shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\n\nmsg['payload'] = 'created with scope: ' + scope\n\nreturn msg", "outputs": 1, "x": 300, "y": 820, "wires": [ [] ] }, { "id": "80175eb8dc6ad009", "type": "inject", "z": "c8e7ecb5849edb9a", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 140, "y": 820, "wires": [ [ "4c7fa5b5b27b83a5" ] ] }, { "id": "3f42560297fe6978", "type": "ui_template", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "name": "Download LOG", "order": 9, "width": 6, "height": 1, "format": "\n
Download error log\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 140, "y": 760, "wires": [ [] ] }, { "id": "c94623ddd9d95f78", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "get update", "func": "from OpenScan import save\n\nif msg['status'] == \"No new update available\":\n save('updateable',False)\nelif msg['status'] == \"New update available\":\n save('updateable',True)\n", "outputs": 1, "x": 170, "y": 60, "wires": [ [] ] }, { "id": "39a502b38837273d", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "", "links": [ "1e7457ea9c2c5e09" ], "x": 205, "y": 300, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "901e31453b2bdff8", "type": "delay", "z": "c8e7ecb5849edb9a", "name": "", "pauseType": "delay", "timeout": "10", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 180, "y": 440, "wires": [ [ "2bb5fe78e09fec8a" ] ] }, { "id": "f983854748ee4763", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 3, "width": 0, "height": 0, "name": "", "label": "", "format": "{{msg.osdate}}", "layout": "row-spread", "className": "", "x": 450, "y": 520, "wires": [] }, { "id": "5347c7c517f5e8c7", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 4, "width": 0, "height": 0, "name": "", "label": "CPU temp:", "format": "{{msg.temp}}", "layout": "row-spread", "className": "", "x": 470, "y": 560, "wires": [] }, { "id": "3a5016f7003cd72c", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 5, "width": 0, "height": 0, "name": "", "label": "CPU memory:", "format": "{{msg.cpu}}", "layout": "row-spread", "className": "", "x": 480, "y": 600, "wires": [] }, { "id": "6d720c4a4ecd9475", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 6, "width": 0, "height": 0, "name": "", "label": "Swap memory:", "format": "{{msg.swap}}", "layout": "row-spread", "className": "", "x": 480, "y": 640, "wires": [] }, { "id": "6438b7d060a70d81", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 7, "width": 0, "height": 0, "name": "", "label": "Diskspace:", "format": "{{msg.diskspace}}", "layout": "row-spread", "className": "", "x": 470, "y": 680, "wires": [] }, { "id": "d7362e6e0ec7bdaa", "type": "inject", "z": "c8e7ecb5849edb9a", "name": "", "props": [ { "p": "overwrite", "v": "true", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 130, "y": 900, "wires": [ [ "4ce127c61c3c5966", "beacc3dc5398fa79" ] ] }, { "id": "4ce127c61c3c5966", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "prepare image creation", "func": "import os\n\n#factory reset, reset wpa, create wpa in boot, rm files\n#should be done before creating a new raspbian image\n\nbasepath = '/home/pi/OpenScan/'\n\n#remove files\n\ndir = basepath + 'scans/'\n\nfor i in ['scans/','tmp/']:\n os.system('rm -r ' + basepath + i)\n os.mkdir(basepath + i)\n\n#delete wifi\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\nwith open(temp_dir, 'w+') as file:\n file.write('update_config=1\\nctrl_interface=DIR=/var/run/wpa_supplicant\\ncountry=de\\n\\n')\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\nos.system('wpa_cli -i wlan0 reconfigure')\n\n#create new wpa_supplicant.conf\nwith open('/boot/wpa_supplicant.conf','w+') as file:\n file.write('country=de\\nupdate_config=1\\nctrl_interface=/var/run/wpa_supplicant\\n\\nnetwork={\\n scan_ssid=1\\n ssid=\"wlan name\"\\n psk=\"xxxx\"\\n}')\n\n#rm tmp dir\n\n\n#stop photos:\nos.system('systemctl stop flask')\nos.system('rm -r ' + basepath + 'tmp')\nos.system('mkdir ' + basepath + 'tmp')\n\nos.system('systemctl stop nodered')\n\n#reset factory\n\n", "outputs": 1, "x": 330, "y": 900, "wires": [ [] ] }, { "id": "beacc3dc5398fa79", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "38783aea9cc317a6" ], "x": 235, "y": 940, "wires": [] }, { "id": "8d012912f302be85", "type": "ui_button", "z": "c8e7ecb5849edb9a", "name": "", "group": "ddbd496e.93a288", "order": 8, "width": 6, "height": 1, "passthru": false, "label": "Show Details/Changelog", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 170, "y": 340, "wires": [ [ "5242607a723cc628" ] ] }, { "id": "5242607a723cc628", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "Changelog", "func": "import requests\n\ntempfile = '/home/pi/OpenScan/tmp/changelog'\n\nurl = 'https://raw.githubusercontent.com/OpenScan-org/OpenScan-Doc/main/docs/changelog.md'\nr = requests.get(url, allow_redirects=False)\n\nwith open(tempfile,'wb') as file:\n file.write(r.content)\n \nwith open(tempfile, 'r') as file:\n text = file.read()\n \ntext = text.replace('\\n','
').replace('*', '  - ')\nmsg['payload'] = text\n\nreturn msg", "outputs": 1, "x": 390, "y": 340, "wires": [ [ "573722197b15bf84" ] ] }, { "id": "573722197b15bf84", "type": "ui_toast", "z": "c8e7ecb5849edb9a", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": true, "className": "", "topic": "", "name": "", "x": 570, "y": 340, "wires": [ [] ] }, { "id": "c745f4a695e8ac45", "type": "inject", "z": "c8e7ecb5849edb9a", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "main", "payloadType": "str", "x": 370, "y": 60, "wires": [ [ "1e10b387ee30c486" ] ] } ] ================================================ FILE: update/beta/settings.js ================================================ /** * Node-RED Settings created at Mon, 24 Jan 2022 08:17:31 GMT * * It can contain any valid JavaScript code that will get run when Node-RED * is started. * * Lines that start with // are commented out. * Each entry should be separated from the entries above and below by a comma ',' * * For more information about individual settings, refer to the documentation: * https://nodered.org/docs/user-guide/runtime/configuration * * The settings are split into the following sections: * - Flow File and User Directory Settings * - Security * - Server Settings * - Runtime Settings * - Editor Settings * - Node Settings * **/ module.exports = { /******************************************************************************* * Flow File and User Directory Settings * - flowFile * - credentialSecret * - flowFilePretty * - userDir * - nodesDir ******************************************************************************/ /** The file containing the flows. If not set, defaults to flows_.json **/ flowFile: "flows.json", /** By default, credentials are encrypted in storage using a generated key. To * specify your own secret, set the following property. * If you want to disable encryption of credentials, set this property to false. * Note: once you set this property, do not change it - doing so will prevent * node-red from being able to decrypt your existing credentials and they will be * lost. */ credentialSecret: false, /** By default, the flow JSON will be formatted over multiple lines making * it easier to compare changes when using version control. * To disable pretty-printing of the JSON set the following property to false. */ flowFilePretty: true, /** By default, all user data is stored in a directory called `.node-red` under * the user's home directory. To use a different location, the following * property can be used */ //userDir: '/home/nol/.node-red/', userDir: '/home/pi/OpenScan/settings/.node-red/', /** Node-RED scans the `nodes` directory in the userDir to find local node files. * The following property can be used to specify an additional directory to scan. */ //nodesDir: '/home/nol/.node-red/nodes', /******************************************************************************* * Security * - adminAuth * - https * - httpsRefreshInterval * - requireHttps * - httpNodeAuth * - httpStaticAuth ******************************************************************************/ /** To password protect the Node-RED editor and admin API, the following * property can be used. See http://nodered.org/docs/security.html for details. */ //adminAuth: { // type: "credentials", // users: [{ // username: "admin", // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", // permissions: "*" // }] //}, /** The following property can be used to enable HTTPS * This property can be either an object, containing both a (private) key * and a (public) certificate, or a function that returns such an object. * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener * for details of its contents. */ /** Option 1: static object */ //https: { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') //}, /** Option 2: function that returns the HTTP configuration object */ // https: function() { // // This function should return the options object, or a Promise // // that resolves to the options object // return { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') // } // }, /** If the `https` setting is a function, the following setting can be used * to set how often, in hours, the function will be called. That can be used * to refresh any certificates. */ //httpsRefreshInterval : 12, /** The following property can be used to cause insecure HTTP connections to * be redirected to HTTPS. */ //requireHttps: true, /** To password protect the node-defined HTTP endpoints (httpNodeRoot), * including node-red-dashboard, or the static content (httpStatic), the * following properties can be used. * The `pass` field is a bcrypt hash of the password. * See http://nodered.org/docs/security.html#generating-the-password-hash */ //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, /******************************************************************************* * Server Settings * - uiPort * - uiHost * - apiMaxLength * - httpServerOptions * - httpAdminRoot * - httpAdminMiddleware * - httpNodeRoot * - httpNodeCors * - httpNodeMiddleware * - httpStatic ******************************************************************************/ /** the tcp port that the Node-RED web server is listening on */ // uiPort: process.env.PORT || 1880, uiPort: process.env.PORT || 80, /** By default, the Node-RED UI accepts connections on all IPv4 interfaces. * To listen on all IPv6 addresses, set uiHost to "::", * The following property can be used to listen on a specific interface. For * example, the following would only allow connections from the local machine. */ //uiHost: "127.0.0.1", /** The maximum size of HTTP request that will be accepted by the runtime api. * Default: 5mb */ //apiMaxLength: '5mb', /** The following property can be used to pass custom options to the Express.js * server used by Node-RED. For a full list of available options, refer * to http://expressjs.com/en/api.html#app.settings.table */ //httpServerOptions: { }, /** By default, the Node-RED UI is available at http://localhost:1880/ * The following property can be used to specify a different root path. * If set to false, this is disabled. */ //httpAdminRoot: '/admin', httpAdminRoot: '/editor', /** The following property can be used to add a custom middleware function * in front of all admin http routes. For example, to set custom http * headers. It can be a single function or an array of middleware functions. */ // httpAdminMiddleware: function(req,res,next) { // // Set the X-Frame-Options header to limit where the editor // // can be embedded // //res.set('X-Frame-Options', 'sameorigin'); // next(); // }, /** Some nodes, such as HTTP In, can be used to listen for incoming http requests. * By default, these are served relative to '/'. The following property * can be used to specifiy a different root path. If set to false, this is * disabled. */ //httpNodeRoot: '/red-nodes', /** The following property can be used to configure cross-origin resource sharing * in the HTTP nodes. * See https://github.com/troygoode/node-cors#configuration-options for * details on its contents. The following is a basic permissive set of options: */ //httpNodeCors: { // origin: "*", // methods: "GET,PUT,POST,DELETE" //}, /** If you need to set an http proxy please set an environment variable * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. * For example - http_proxy=http://myproxy.com:8080 * (Setting it here will have no effect) * You may also specify no_proxy (or NO_PROXY) to supply a comma separated * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk */ /** The following property can be used to add a custom middleware function * in front of all http in nodes. This allows custom authentication to be * applied to all http in nodes, or any other sort of common request processing. * It can be a single function or an array of middleware functions. */ //httpNodeMiddleware: function(req,res,next) { // // Handle/reject the request, or pass it on to the http in node by calling next(); // // Optionally skip our rawBodyParser by setting this to true; // //req.skipRawBodyParser = true; // next(); //}, /** When httpAdminRoot is used to move the UI to a different root path, the * following property can be used to identify a directory of static content * that should be served at http://localhost:1880/. */ //httpStatic: '/home/nol/node-red-static/', httpStatic: '/home/pi/OpenScan/', /******************************************************************************* * Runtime Settings * - lang * - logging * - contextStorage * - exportGlobalContextKeys * - externalModules ******************************************************************************/ /** Uncomment the following to run node-red in your preferred language. * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko * Some languages are more complete than others. */ // lang: "de", /** Configure the logging output */ logging: { /** Only console logging is currently supported */ console: { /** Level of logging to be recorded. Options are: * fatal - only those errors which make the application unusable should be recorded * error - record errors which are deemed fatal for a particular request + fatal errors * warn - record problems which are non fatal + errors + fatal errors * info - record information about the general running of the application + warn + error + fatal errors * debug - record information which is more verbose than info + info + warn + error + fatal errors * trace - record very detailed logging + debug + info + warn + error + fatal errors * off - turn off all logging (doesn't affect metrics or audit) */ level: "info", /** Whether or not to include metric events in the log output */ metrics: false, /** Whether or not to include audit events in the log output */ audit: false } }, /** Context Storage * The following property can be used to enable context storage. The configuration * provided here will enable file-based context that flushes to disk every 30 seconds. * Refer to the documentation for further options: https://nodered.org/docs/api/context/ */ //contextStorage: { // default: { // module:"localfilesystem" // }, //}, /** `global.keys()` returns a list of all properties set in global context. * This allows them to be displayed in the Context Sidebar within the editor. * In some circumstances it is not desirable to expose them to the editor. The * following property can be used to hide any property set in `functionGlobalContext` * from being list by `global.keys()`. * By default, the property is set to false to avoid accidental exposure of * their values. Setting this to true will cause the keys to be listed. */ exportGlobalContextKeys: false, /** Configure how the runtime will handle external npm modules. * This covers: * - whether the editor will allow new node modules to be installed * - whether nodes, such as the Function node are allowed to have their * own dynamically configured dependencies. * The allow/denyList options can be used to limit what modules the runtime * will install/load. It can use '*' as a wildcard that matches anything. */ externalModules: { // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ // palette: { /** Configuration for the Palette Manager */ // allowInstall: true, /** Enable the Palette Manager in the editor */ // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ // allowList: [], // denyList: [] // }, // modules: { /** Configuration for node-specified modules */ // allowInstall: true, // allowList: [], // denyList: [] // } }, /******************************************************************************* * Editor Settings * - disableEditor * - editorTheme ******************************************************************************/ /** The following property can be used to disable the editor. The admin API * is not affected by this option. To disable both the editor and the admin * API, use either the httpRoot or httpAdminRoot properties */ //disableEditor: false, /** Customising the editor * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes * for all available options. */ editorTheme: { /** The following property can be used to set a custom theme for the editor. * See https://github.com/node-red-contrib-themes/theme-collection for * a collection of themes to chose from. */ //theme: "", palette: { /** The following property can be used to order the categories in the editor * palette. If a node's category is not in the list, the category will get * added to the end of the palette. * If not set, the following default order is used: */ //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], }, projects: { /** To enable the Projects feature, set this value to true */ enabled: false, workflow: { /** Set the default projects workflow mode. * - manual - you must manually commit changes * - auto - changes are automatically committed * This can be overridden per-user from the 'Git config' * section of 'User Settings' within the editor */ mode: "manual" } }, codeEditor: { /** Select the text editor component used by the editor. * Defaults to "ace", but can be set to "ace" or "monaco" */ lib: "ace", options: { /** The follow options only apply if the editor is set to "monaco" * * theme - must match the file name of a theme in * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme" */ theme: "vs", /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc. * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html */ //fontSize: 14, //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", //fontLigatures: true, } } }, /******************************************************************************* * Node Settings * - fileWorkingDirectory * - functionGlobalContext * - functionExternalModules * - nodeMessageBufferMaxLength * - ui (for use with Node-RED Dashboard) * - debugUseColors * - debugMaxLength * - execMaxBufferSize * - httpRequestTimeout * - mqttReconnectTime * - serialReconnectTime * - socketReconnectTime * - socketTimeout * - tcpMsgQueueSize * - inboundWebSocketTimeout * - tlsConfigDisableLocalFiles * - webSocketNodeVerifyClient ******************************************************************************/ /** The working directory to handle relative file paths from within the File nodes * defaults to the working directory of the Node-RED process. */ //fileWorkingDirectory: "", /** Allow the Function node to load additional npm modules directly */ functionExternalModules: true, /** The following property can be used to set predefined values in Global Context. * This allows extra node modules to be made available with in Function node. * For example, the following: * functionGlobalContext: { os:require('os') } * will allow the `os` module to be accessed in a Function node using: * global.get("os") */ functionGlobalContext: { os:require('os'), path:require('path'), fs:require('fs'), }, /** The maximum number of messages nodes will buffer internally as part of their * operation. This applies across a range of nodes that operate on message sequences. * defaults to no limit. A value of 0 also means no limit is applied. */ //nodeMessageBufferMaxLength: 0, /** If you installed the optional node-red-dashboard you can set it's path * relative to httpNodeRoot * Other optional properties include * readOnly:{boolean}, * middleware:{function or array}, (req,res,next) - http middleware * ioMiddleware:{function or array}, (socket,next) - socket.io middleware */ //ui: { path: "ui" }, ui: { path: "" }, /** Colourise the console output of the debug node */ //debugUseColors: true, /** The maximum length, in characters, of any message sent to the debug sidebar tab */ debugMaxLength: 1000, /** Maximum buffer size for the exec node. Defaults to 10Mb */ //execMaxBufferSize: 10000000, /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */ //httpRequestTimeout: 120000, /** Retry time in milliseconds for MQTT connections */ mqttReconnectTime: 15000, /** Retry time in milliseconds for Serial port connections */ serialReconnectTime: 15000, /** Retry time in milliseconds for TCP socket connections */ //socketReconnectTime: 10000, /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */ //socketTimeout: 120000, /** Maximum number of messages to wait in queue while attempting to connect to TCP socket * defaults to 1000 */ //tcpMsgQueueSize: 2000, /** Timeout in milliseconds for inbound WebSocket connections that do not * match any configured node. Defaults to 5000 */ //inboundWebSocketTimeout: 5000, /** To disable the option for using local files for storing keys and * certificates in the TLS configuration node, set this to true. */ //tlsConfigDisableLocalFiles: true, /** The following property can be used to verify websocket connection attempts. * This allows, for example, the HTTP request headers to be checked to ensure * they include valid authentication information. */ //webSocketNodeVerifyClient: function(info) { // /** 'info' has three properties: // * - origin : the value in the Origin header // * - req : the HTTP request // * - secure : true if req.connection.authorized or req.connection.encrypted is set // * // * The function should return true if the connection should be accepted, false otherwise. // * // * Alternatively, if this function is defined to accept a second argument, callback, // * it can be used to verify the client asynchronously. // * The callback takes three arguments: // * - result : boolean, whether to accept the connection or not // * - code : if result is false, the HTTP error status to return // * - reason: if result is false, the HTTP reason string to return // */ //}, } ================================================ FILE: update/betaArdu/Arducam.py ================================================ import time import os try: import v4l2 except Exception as e: print(e) print("Try to install v4l2-fix") try: from pip import main as pipmain except ImportError: from pip._internal import main as pipmain pipmain(['install', 'v4l2-fix']) print("\nTry to run the focus program again.") exit(0) import fcntl import errno # # Type # v4l2.V4L2_CTRL_TYPE_INTEGER # v4l2.V4L2_CTRL_TYPE_BOOLEAN # v4l2.V4L2_CTRL_TYPE_MENU # v4l2.V4L2_CTRL_TYPE_BUTTON # v4l2.V4L2_CTRL_TYPE_INTEGER64 # v4l2.V4L2_CTRL_TYPE_CTRL_CLASS # # Flags # v4l2.V4L2_CTRL_FLAG_DISABLED # v4l2.V4L2_CTRL_FLAG_GRABBED # v4l2.V4L2_CTRL_FLAG_READ_ONLY # v4l2.V4L2_CTRL_FLAG_UPDATE # v4l2.V4L2_CTRL_FLAG_INACTIVE # v4l2.V4L2_CTRL_FLAG_SLIDER def assert_valid_queryctrl(queryctrl): return queryctrl.type & ( v4l2.V4L2_CTRL_TYPE_INTEGER | v4l2.V4L2_CTRL_TYPE_BOOLEAN | v4l2.V4L2_CTRL_TYPE_MENU | v4l2.V4L2_CTRL_TYPE_BUTTON | v4l2.V4L2_CTRL_TYPE_INTEGER64 | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS | 7 | 8 | 9 ) and queryctrl.flags & ( v4l2.V4L2_CTRL_FLAG_DISABLED | v4l2.V4L2_CTRL_FLAG_GRABBED | v4l2.V4L2_CTRL_FLAG_READ_ONLY | v4l2.V4L2_CTRL_FLAG_UPDATE | v4l2.V4L2_CTRL_FLAG_INACTIVE | v4l2.V4L2_CTRL_FLAG_SLIDER ) def get_device_controls_menu(fd, queryctrl): querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum) while querymenu.index <= queryctrl.maximum: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu) yield querymenu querymenu.index += 1 def get_device_controls_by_class(fd, control_class): # enumeration by control class queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) while True: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) except IOError as e: assert e.errno == errno.EINVAL break if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class: break yield queryctrl queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) def getdict(struct): val = dict((field, getattr(struct, field)) for field, _ in struct._fields_) val.pop("reserved") return val def get_device_controls(fd): # original enumeration method queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE) while queryctrl.id < v4l2.V4L2_CID_LASTP1: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) print(queryctrl.name) except IOError as e: # this predefined control is not supported by this device assert e.errno == errno.EINVAL queryctrl.id += 1 continue queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1) def get_ctrls(vd): ctrls = [] # enumeration by control class for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA): for queryctrl in get_device_controls_by_class(vd, class_): ctrl = getdict(queryctrl) if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): # print(querymenu.name) ctrl["menu"].append(querymenu.name) if queryctrl.type == 9: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): ctrl["menu"].append(querymenu.index) ctrls.append(ctrl) return ctrls def set_ctrl(vd, id, value): ctrl = v4l2.v4l2_control() ctrl.id = id ctrl.value = value try: fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl) except IOError as e: print(e) def get_ctrl(vd, id): ctrl = v4l2.v4l2_control() ctrl.id = id try: fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl) except IOError as e: print(e) return None return ctrl.value class Focuser: FOCUS_ID = 0x009a090a dev = None def __init__(self, dev=0): self.focus_value = 0 self.dev = dev if type(dev) == int or (type(dev) == str and dev.isnumeric()): self.dev = "/dev/video{}".format(dev) self.fd = open(self.dev, 'r') self.ctrls = get_ctrls(self.fd) self.hasFocus = False for ctrl in self.ctrls: if ctrl['id'] == Focuser.FOCUS_ID: self.hasFocus = True self.opts[Focuser.OPT_FOCUS]["MIN_VALUE"] = ctrl['minimum'] self.opts[Focuser.OPT_FOCUS]["MAX_VALUE"] = ctrl['maximum'] self.opts[Focuser.OPT_FOCUS]["DEF_VALUE"] = ctrl['default'] self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID) if not self.hasFocus: raise RuntimeError("Device {} has no focus_absolute control.".format(self.dev)) def read(self): return self.focus_value def write(self, value): self.focus_value = value # os.system("v4l2-ctl -d {} -c focus_absolute={}".format(self.dev, value)) set_ctrl(self.fd, Focuser.FOCUS_ID, value) OPT_BASE = 0x1000 OPT_FOCUS = OPT_BASE | 0x01 OPT_ZOOM = OPT_BASE | 0x02 OPT_MOTOR_X = OPT_BASE | 0x03 OPT_MOTOR_Y = OPT_BASE | 0x04 OPT_IRCUT = OPT_BASE | 0x05 opts = { OPT_FOCUS : { "MIN_VALUE": 0, "MAX_VALUE": 1000, "DEF_VALUE": 0, }, } def reset(self,opt,flag = 1): info = self.opts[opt] if info == None or info["DEF_VALUE"] == None: return self.set(opt,info["DEF_VALUE"]) def get(self,opt,flag = 0): info = self.opts[opt] return self.read() def set(self,opt,value,flag = 1): info = self.opts[opt] if value > info["MAX_VALUE"]: value = info["MAX_VALUE"] elif value < info["MIN_VALUE"]: value = info["MIN_VALUE"] self.write(value) print("write: {}".format(value)) def __del__(self): self.fd.close() pass ================================================ FILE: update/betaArdu/OpenScan.py ================================================ basepath = '/home/pi/OpenScan/' from os.path import isfile import os def load_bool(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') if value == '1' or value == 'True' or value =='true': value = True else: value = False return value def fade_led(pin_led, fade_steps, duty_max, dir = True): import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(pin_led, GPIO.OUT) pwm = GPIO.PWM(pin_led, 200) if dir: pwm.start(0) for duty_cycle in range(0, fade_steps*10, 1): # Increase duty cycle in steps pwm.ChangeDutyCycle(duty_max*duty_cycle/(10*fade_steps)) time.sleep(0.001) # Pause between steps (adjust as needed) else: pwm.start(duty_max) for duty_cycle in range(fade_steps*10,0, -1): # Increase duty cycle in steps pwm.ChangeDutyCycle(duty_max*duty_cycle/(10*fade_steps)) time.sleep(0.001) # Pause between steps (adjust as needed) pwm.stop() def check_hotspot_mode(interface="wlan0"): import subprocess try: output = subprocess.check_output(["iwconfig", interface]).decode("utf-8") if "Mode:Master" in output: return True elif "Mode:Managed" in output: return False else: return False except subprocess.CalledProcessError as e: return False def add_wifi_network(ssid, password, country): import re conf_file = "/etc/wpa_supplicant/wpa_supplicant-wlan0.conf" if not os.path.exists(conf_file): return False if not (ssid and password and country): return False with open(conf_file, "r") as f: content = f.read() updated_content = re.sub(r'country=\w+', f'country={country}', content) if f'ssid="{ssid}"' in content: network_block_pattern = re.compile( r'network=\{\s*ssid="' + re.escape(ssid) + r'".*?psk=".*?".*?\}', re.DOTALL ) updated_network_block = f'network={{\n ssid="{ssid}"\n psk="{password}"\n key_mgmt=WPA-PSK\n}}' updated_content = network_block_pattern.sub(updated_network_block, updated_content) else: network_block = f'\nnetwork={{\n ssid="{ssid}"\n psk="{password}"\n key_mgmt=WPA-PSK\n}}\n' updated_content += network_block with open(conf_file, "w") as f: f.write(updated_content) os.system("sudo systemctl restart wpa_supplicant@wlan0") return True def load_str(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') return value def load_int(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = int(file.read().replace('\n','')) return value def load_float(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = float(file.read().replace('\n','')) return value def save(name, value): filename = basepath+'settings/'+name with open(filename, 'w+') as file: file.write(str(value)) return def OpenScanCloud(cmd, msg): from requests import get osc_user = 'openscan' osc_pw = 'free' osc_server = 'http://openscanfeedback.dnsuser.de:1334/' try: r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg) except: r = type('obj', (object,), {'status_code' : 404, 'text':None}) return r def camera(cmd, msg = {}): from requests import get flask = 'http://127.0.0.1:1312/' try: r = get(flask + cmd, params=msg) return r.status_code except: return 400 def motorrun(motor,angle,ES_enable=False,ES_start_state = True): #motor can be "rotor", "tt" or "extra" import RPi.GPIO as GPIO from time import sleep from math import cos msg = {'cmd':'set'} GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) spr = load_int(motor + '_stepsperrotation') dirpin = load_int('pin_' + motor + '_dir') steppin = load_int('pin_' + motor +'_step') ES_pin = load_int('pin_' + motor + '_endstop') dir = load_int(motor + '_dir') ramp = load_int(motor + '_accramp') acc = load_float(motor + '_acc') delay_init = load_float(motor + '_delay') delay = delay_init step_count=int(angle*spr/360) * dir GPIO.setup(dirpin, GPIO.OUT) GPIO.setup(steppin, GPIO.OUT) GPIO.setup(ES_pin, GPIO.IN, pull_up_down = GPIO.PUD_UP) if (step_count>0): GPIO.output(dirpin, GPIO.HIGH) if(step_count<0): GPIO.output(dirpin, GPIO.LOW) step_count=-step_count for x in range(step_count): if ES_enable == True and GPIO.input(ES_pin) != ES_start_state: i = 0 while i <= 10: if GPIO.input(ES_pin) == ES_start_state: i = 11 if i == 10: return i = i + 1 GPIO.output(steppin, GPIO.HIGH) if x<=ramp and x<=step_count/2: delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc) #delay=delay_init+(ramp-x)*(delay_init)/acc elif step_count-x<=ramp and x>step_count/2: delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc) #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc else: delay = delay_init sleep(delay) GPIO.output(steppin, GPIO.LOW) sleep(delay) def ringlight(number,state): import RPi.GPIO as GPIO msg = {'cmd':'set'} pin = load_int('pin_ringlight' + str(number)) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, state) def take_photo(file): from os import system filepath = basepath + file model=load_str('model') shutter = str(load_int('cam_shutter')) saturation = load_str('cam_saturation') contrast = load_str('cam_contrast') awbg_red = load_str('cam_awbg_red') awbg_blue = load_str('cam_awbg_blue') gain = load_str('cam_gain') quality = load_int('cam_jpeg_quality') filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg' #width = load_str('cam_resx') #height = load_str('cam_resy') timeout = load_str('cam_timeout') cropx = load_int('cam_cropx')/200 cropy = load_int('cam_cropy')/200 rotation = load_int('cam_rotation') AF = load_bool('cam_AFmode') camera = load_str('camera') if camera == 'imx519' and AF == True: autofocus = ' --autofocus ' else: autofocus = '' if camera == "usb_webcam": cmd = 'fswebcam -i 0 -r "1280x720" -F 5 --no-banner --jpeg 95 --save ' + filepath2 else: cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1' # cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus system(cmd) return cmd def get_points(samples=1): from math import pi, sqrt, acos, atan2, cos, sin points = [] phi = pi * (3. - sqrt(5.)) for i in range(int(samples)): y = 1 - (i / float(samples - 1)) * 2 radius = sqrt(1 - y * y) theta = phi * i x = cos(theta) * radius z = sin(theta) * radius r=sqrt(x*x+y*y+z*z) theta_neu=acos(z/r)*180/pi phi_neu=atan2(y,x)*180/pi points.append((theta_neu-90,phi_neu)) points.sort() return points def create_coordinates(angle_min, angle_max,point_count): point_count_final=point_count if angle_max < angle_min: a = angle_min angle_min = angle_max angle_max = a point_count=point_count*90/(angle_max-angle_min) actual_points=0 while actual_pointsangle_min and x20: point_count=point_count+3 else: point_count=point_count+1 return filtered def haversine_distance_deg(theta1, phi1, theta2, phi2): import numpy as np R = 1 dtheta = np.radians(theta2 - theta1) dphi = np.radians(phi2 - phi1) theta1, phi1 = np.radians(theta1), np.radians(phi1) theta2, phi2 = np.radians(theta2), np.radians(phi2) a = np.sin(dtheta / 2) ** 2 + np.cos(theta1) * np.cos(theta2) * np.sin(dphi / 2) ** 2 c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) return R * c def sort_spherical_coordinates_deg(points_spherical_deg): import numpy as np from tsp_solver.greedy import solve_tsp points_spherical_deg = np.array(points_spherical_deg) # Convert list of tuples to NumPy array n = len(points_spherical_deg) dist_matrix = np.zeros((n, n)) # Calculate haversine distance for each pair of points for i in range(n): for j in range(i + 1, n): dist = haversine_distance_deg(points_spherical_deg[i, 0], points_spherical_deg[i, 1], points_spherical_deg[j, 0], points_spherical_deg[j, 1]) dist_matrix[i, j] = dist dist_matrix[j, i] = dist # Solve the TSP problem using the tsp_solver.greedy algorithm path = solve_tsp(dist_matrix) sorted_points_spherical_deg = points_spherical_deg[path] # Convert the sorted NumPy array back to a list of tuples return [tuple(point) for point in sorted_points_spherical_deg] ================================================ FILE: update/betaArdu/config.txt ================================================ # For more options and information see # http://rpf.io/configtxt # Some settings may impact device functionality. See link above for details # uncomment if you get no picture on HDMI for a default "safe" mode #hdmi_safe=1 # uncomment the following to adjust overscan. Use positive numbers if console # goes off screen, and negative if there is too much border #overscan_left=16 #overscan_right=16 #overscan_top=16 #overscan_bottom=16 # uncomment to force a console size. By default it will be display's size minus # overscan. #framebuffer_width=1280 #framebuffer_height=720 # uncomment if hdmi display is not detected and composite is being output #hdmi_force_hotplug=1 # uncomment to force a specific HDMI mode (this will force VGA) #hdmi_group=1 #hdmi_mode=1 # uncomment to force a HDMI mode rather than DVI. This can make audio work in # DMT (computer monitor) modes #hdmi_drive=2 # uncomment to increase signal to HDMI, if you have interference, blanking, or # no display #config_hdmi_boost=4 # uncomment for composite PAL #sdtv_mode=2 #uncomment to overclock the arm. 700 MHz is the default. #arm_freq=800 # Uncomment some or all of these to enable the optional hardware interfaces #dtparam=i2c_arm=on #dtparam=i2s=on #dtparam=spi=on # Uncomment this to enable infrared communication. #dtoverlay=gpio-ir,gpio_pin=17 #dtoverlay=gpio-ir-tx,gpio_pin=18 # Additional overlays and parameters are documented /boot/overlays/README # Enable audio (loads snd_bcm2835) dtparam=audio=on # Automatically load overlays for detected cameras camera_auto_detect=1 # Automatically load overlays for detected DSI displays display_auto_detect=1 # Enable DRM VC4 V3D driver dtoverlay=vc4-kms-v3d max_framebuffers=2 # Disable compensation for displays with overscan disable_overscan=1 [cm4] # Enable host mode on the 2711 built-in XHCI USB controller. # This line should be removed if the legacy DWC2 controller is required # (e.g. for USB device mode) or if USB support is not required. otg_mode=1 [all] [pi4] # Run as fast as firmware / board allows arm_boost=1 [all] camera_auto_detect=0 gpu_mem=256 dtoverlay=vc4-fkms-v3d dtoverlay=imx519 #dtoverlay=imx519,media-controller=1 ================================================ FILE: update/betaArdu/fla.py ================================================ from flask import Flask, make_response, jsonify, request, abort from picamera2 import Picamera2 from PIL import Image, ImageDraw, ImageOps, ImageFilter, ImageEnhance, ImageChops, ImageFont from time import sleep, time import shutil from OpenScan import load_int, load_float, load_bool, ringlight import RPi.GPIO as GPIO from math import sqrt import os import math from skimage import io, feature, color, transform import numpy as np from scipy import ndimage GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) app = Flask(__name__) basedir = '/home/pi/OpenScan/' timer = time() cam_mode = 0 def overlay_mask(image, mask_image): # Ensure image is in RGB mode image_rgb = image.convert('RGB') # Create an empty image with RGBA channels overlay = Image.new('RGBA', image_rgb.size) # Prepare a red image of the same size red_image = Image.new('RGB', image_rgb.size, (255, 0, 0)) # Prepare a mask where the condition is met (mask_image pixels == 255) mask_condition = np.array(mask_image) > 0 overlay_mask = Image.fromarray(np.uint8(mask_condition) * 255) # Paste the red image onto the overlay using the condition mask overlay.paste(red_image, mask=overlay_mask) # Combine the original image with the overlay combined = Image.alpha_composite(image_rgb.convert('RGBA'), overlay) # Convert the final image to RGB combined_rgb = combined.convert('RGB') return combined_rgb def highlight_sharpest_areas(image, threshold=load_int('cam_sharpness'), dilation_size=5): # Convert PIL image to grayscale image_gray = image.convert('L') # Convert grayscale image to numpy array image_array = np.array(image_gray) # Calculate the gradient using a Sobel filter dx = ndimage.sobel(image_array, 0) # horizontal derivative dy = ndimage.sobel(image_array, 1) # vertical derivative mag = np.hypot(dx, dy) # magnitude # Threshold the gradient to create a mask of the sharpest areas mask = np.where(mag > threshold, 255, 0).astype(np.uint8) dilated_mask = ndimage.binary_dilation(mask, structure=np.ones((dilation_size,dilation_size))) # Create a PIL image from the mask mask_image = Image.fromarray(dilated_mask) return mask_image ################################################################################################################### @app.route('/shutdown', methods=['get']) def shutdown(): delay = 0.1 ringlight(2,False) for i in range (5): ringlight(1,True) sleep(delay) ringlight(1,False) sleep(delay) os.system('shutdown -h now') ################################################################################################################### @app.route('/reboot', methods=['get']) def reboot(): delay = 0.1 ringlight(2,False) for i in range (5): ringlight(1,True) sleep(delay) ringlight(1,False) sleep(delay) os.system('reboot -h') ################################################################################################################### def plot_orb_keypoints(pil_image): downscale = 2 # Read the image from the given image path image = np.array(pil_image) #image = io.imread(image_path) image = transform.resize(image, (image.shape[0] // downscale, image.shape[1] // downscale), anti_aliasing=True) # Convert the image to grayscale gray_image = color.rgb2gray(image) try: orb = feature.ORB(n_keypoints=10000, downscale=1.2, fast_n=2, fast_threshold=0.2 , n_scales=3, harris_k=0.001) orb.detect_and_extract(gray_image) keypoints = orb.keypoints except: return pil_image # Convert the image back to the range [0, 255] display_image = (image * 255).astype(np.uint8) # Draw the keypoints on the image draw = ImageDraw.Draw(pil_image) size = max(2,int(image.shape[0]*downscale*0.005)) for i, (y, x) in enumerate(keypoints): draw.ellipse([(downscale*x-size, downscale*y-size), (downscale*x+size, downscale*y+size)], fill = (0,255,0)) # Save the image with keypoints to the given output path return pil_image def add_histo(img): histo_size = 241 img_gray = ImageOps.grayscale(img) histogram = img_gray.histogram() histogram_log = [math.log10(h + 1) for h in histogram] histogram_max = max(histogram_log) histogram_normalized = [float(h) / histogram_max for h in histogram_log] hist_image = Image.new("RGBA", (histo_size, histo_size), (255, 255, 255, 0)) draw = ImageDraw.Draw(hist_image) for i in range(0, 256): x = i y = 256 - int(histogram_normalized[i] * 256) draw.line((x, 256, x, y), fill=(0, 0, 0, 255)) text = "" if min(histogram[235:238])>0: text = "overexposed" if sum(histogram[190:192])<8: text = "underexposed" font = ImageFont.truetype("DejaVuSans.ttf", 30) bbox = draw.textbbox((0, 0), text, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] x = (hist_image.width - text_width )/2 y = hist_image.height - text_height - 10 draw.text((x, y), text, font=font, fill=(255,0,0)) scale = 0.25 width1, height1 = hist_image.size width2 = img.size[0] new_width1 = int(width2 * scale) new_height1 = int((height1 / width1) * new_width1) hist_image = hist_image.convert('RGB') hist_image = hist_image.resize((new_width1, new_height1)) x = hist_image.width - text_width - 10 y = hist_image.height - text_height - 10 img.paste(hist_image, (img.size[0]-new_width1-int(0.01*img.size[0]),img.size[1]-new_height1-int(0.01*img.size[0]))) return img def create_mask(image: Image, scale: float = 0.1, threshold: int = 45) -> Image: threshold = load_int("cam_mask_threshold") if threshold <= 1: return image orig = image image = image.resize((int(image.width*scale),int(image.height*scale))) image = image.convert("L") reduced = image image = image.filter(ImageFilter.EDGE_ENHANCE) image = image.filter(ImageFilter.BLUR) reduced = reduced.filter(ImageFilter.EDGE_ENHANCE_MORE) mask = ImageChops.difference(image, reduced) mask = ImageEnhance.Brightness(mask).enhance(2.5) mask = mask.filter(ImageFilter.MaxFilter(9)) mask = mask.filter(ImageFilter.MinFilter(5)) mask = mask.point(lambda x: 255 if x wrong aspect ratio! # preview_config = picam2.create_preview_configuration(main={"size": (2028, 1520)}) preview_config = picam2.create_preview_configuration(main={"size": (2028, 1520)}, controls ={"FrameDurationLimits": (1, 1000000)}) # preview_config = picam2.create_preview_configuration(main={"size": (2328, 1748)}) capture_config = picam2.create_still_configuration(controls ={"FrameDurationLimits": (1, 1000000)}) picam2.configure(preview_config) picam2.controls.AnalogueGain = 1.0 picam2.start() return ({}, 200) ################################################################################################################### @app.route('/picam2_take_photo', methods=['get']) def picam2_take_photo(): starttime = time() cropx = load_int('cam_cropx')/200 cropy = load_int('cam_cropy')/200 rotation = load_int('cam_rotation') img = picam2.capture_image() if cam_mode !=1: img = img.convert('RGB') w,h = img.size if cropx != 0 or cropy != 0: img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy))) if rotation == 90: img = img.transpose(Image.ROTATE_90) elif rotation == 180: img= img.transpose(Image.ROTATE_180) elif rotation == 270: img= img.transpose(Image.ROTATE_270) if load_bool("cam_mask"): if cam_mode == 1: downscale = 0.045*1.4 else: downscale = 0.1*1.4 img = create_mask(img, downscale) if load_bool("cam_features") and not load_bool("cam_sharparea"): img = plot_orb_keypoints(img) if load_bool("cam_sharparea") and not load_bool("cam_features"): img2 = highlight_sharpest_areas(img) img = overlay_mask(img, img2) if cam_mode != 1 and not load_bool("cam_sharparea") and not load_bool("cam_features"): img = add_histo(img) img.save("/home/pi/OpenScan/tmp2/preview.jpg", quality=load_int('cam_jpeg_quality')) print("total " + str(int(1000*(time()-starttime))) + "ms") starttime = time() return ({}, 200) ################################################################################################################### @app.route('/picam2_focus', methods=['get']) def picam2_focus(): focus = float(request.args.get('focus')) picam2.set_controls({"AfMode": 0, "LensPosition": focus}) return ({}, 200) ################################################################################################################### @app.route('/picam2_af1', methods=['get']) def picam2_af1(): from libcamera import controls picam2.set_controls({"AfMode": 2 ,"AfTrigger": 0, "AfRange":controls.AfRangeEnum.Macro}) return ({}, 200) ################################################################################################################### @app.route('/picam2_af2', methods=['get']) def picam2_af2(): picam2.set_controls({"AfMode": 2 ,"AfTrigger": 0}) return ({}, 200) ################################################################################################################### @app.route('/picam2_exposure', methods=['get']) def picam2_exposure(): exposure = int(request.args.get('exposure')) picam2.controls.AnalogueGain = 1.0 picam2.controls.ExposureTime = exposure return ({}, 200) ################################################################################################################### @app.route('/picam2_contrast', methods=['get']) def picam2_contrast(): contrast = float(request.args.get('contrast')) picam2.controls.Contrast = contrast return ({}, 200) ################################################################################################################### @app.route('/picam2_saturation', methods=['get']) def picam2_saturation(): saturation = float(request.args.get('saturation')) picam2.controls.Saturation = saturation return ({}, 200) ################################################################################################################### @app.route('/picam2_switch_mode', methods=['get']) def picam2_switch_mode(): global cam_mode cam_mode = int(request.args.get('mode')) if cam_mode == 1: picam2.switch_mode(capture_config) else: picam2.switch_mode(preview_config) return ({}, 200) ################################################################################################################### @app.route('/picam2_show_mode', methods=['get']) def picam2_show_mode(): global cam_mode return({"mode":cam_mode},200) ################################################################################################################### @app.route('/picam2_af', methods=['get']) def picam2_af(): picam2.set_controls({"AfMode": 1 ,"AfTrigger": 0}) # --> wait 3-5s return ({}, 200) if __name__ == '__main__': # app.run(host='127.0.0.1', port=1312, debug=False, threaded=True) app.run(host='0.0.0.0', port=1312, debug=False, threaded=True) ================================================ FILE: update/betaArdu/flows.json ================================================ [ { "id": "e6f4d02efb300ea9", "type": "tab", "label": "Init", "disabled": false, "info": "", "env": [] }, { "id": "481edaf6db5a7a54", "type": "tab", "label": "Scan", "disabled": false, "info": "", "env": [] }, { "id": "80a3942785a26c29", "type": "tab", "label": "Files", "disabled": false, "info": "", "env": [] }, { "id": "e43a27722b508115", "type": "tab", "label": "Settings", "disabled": false, "info": "", "env": [] }, { "id": "a5557543ccff5889", "type": "tab", "label": "Update", "disabled": false, "info": "", "env": [] }, { "id": "90223f7ddc082321", "type": "ui_group", "name": "preview", "tab": "e23b837a9f040895", "order": 2, "disp": false, "width": "7", "collapse": false, "className": "" }, { "id": "e23b837a9f040895", "type": "ui_tab", "name": "Scan", "icon": "dashboard", "order": 2, "disabled": false, "hidden": false }, { "id": "5c06cb6bcc371ee6", "type": "ui_base", "theme": { "name": "theme-dark", "lightTheme": { "default": "#0094CE", "baseColor": "#0094CE", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "darkTheme": { "default": "#097479", "baseColor": "#097479", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "customTheme": { "name": "Untitled Theme 1", "default": "#4B7930", "baseColor": "#4B7930", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "reset": false }, "themeState": { "base-color": { "default": "#097479", "value": "#097479", "edited": false }, "page-titlebar-backgroundColor": { "value": "#097479", "edited": false }, "page-backgroundColor": { "value": "#111111", "edited": false }, "page-sidebar-backgroundColor": { "value": "#333333", "edited": false }, "group-textColor": { "value": "#0eb8c0", "edited": false }, "group-borderColor": { "value": "#555555", "edited": false }, "group-backgroundColor": { "value": "#333333", "edited": false }, "widget-textColor": { "value": "#eeeeee", "edited": false }, "widget-backgroundColor": { "value": "#097479", "edited": false }, "widget-borderColor": { "value": "#333333", "edited": false }, "base-font": { "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" } }, "angularTheme": { "primary": "indigo", "accents": "blue", "warn": "red", "background": "grey", "palette": "light" } }, "site": { "name": "OpenScan", "hideToolbar": "false", "allowSwipe": "false", "lockMenu": "false", "allowTempTheme": "true", "dateFormat": "DD/MM/YYYY", "sizes": { "sx": 48, "sy": 48, "gx": 6, "gy": 6, "cx": 6, "cy": 6, "px": 0, "py": 0 } } }, { "id": "34bc0fd2b0f2416c", "type": "ui_link", "name": "GitHub", "link": "https://openscan-org.github.io/OpenScan-Doc/", "icon": "fa-bookmark", "target": "iframe", "order": 6 }, { "id": "23f75a8768250ce8", "type": "ui_link", "name": "Patreon", "link": "https://www.patreon.com/OpenScan", "icon": "fa-bookmark", "target": "newtab", "order": 5 }, { "id": "b5fdd57b.15eda8", "type": "ui_group", "name": "Main", "tab": "15a222ed.d70a7d", "order": 1, "disp": false, "width": 13, "collapse": false }, { "id": "db43d646.2074c8", "type": "ui_group", "name": "OpenScanCloud", "tab": "15a222ed.d70a7d", "order": 2, "disp": true, "width": "6", "collapse": false }, { "id": "15a222ed.d70a7d", "type": "ui_tab", "name": "Files&Cloud", "icon": "dashboard", "order": 3, "disabled": false, "hidden": false }, { "id": "365a30d0dfa83e95", "type": "ui_group", "name": "settings", "tab": "e23b837a9f040895", "order": 1, "disp": false, "width": 7, "collapse": false, "className": "" }, { "id": "ac7409105cfecac6", "type": "ui_group", "name": "advanced", "tab": "e23b837a9f040895", "order": 3, "disp": false, "width": 7, "collapse": false, "className": "" }, { "id": "729f9ea6e3513c9b", "type": "ui_group", "name": "Home", "tab": "b3150b13e34b1fe8", "order": 2, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "5b3e5aca21140e9a", "type": "ui_group", "name": "Update", "tab": "b3150b13e34b1fe8", "order": 1, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "b3150b13e34b1fe8", "type": "ui_tab", "name": "OpenScan", "icon": "dashboard", "order": 1, "disabled": false, "hidden": true }, { "id": "ddbd496e.93a288", "type": "ui_group", "name": "Manage Updates", "tab": "d25e08b4.5b27e8", "order": 1, "disp": true, "width": "6", "collapse": false }, { "id": "3ce32450.e0cffc", "type": "ui_group", "name": "System & Stats", "tab": "d25e08b4.5b27e8", "order": 2, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "d25e08b4.5b27e8", "type": "ui_tab", "name": "Update & Info", "icon": "dashboard", "order": 4, "disabled": false, "hidden": false }, { "id": "4390b2ebcbbe104c", "type": "ui_group", "name": "General", "tab": "457102eadc9ddb6c", "order": 1, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "8ab79a98e536e0d6", "type": "ui_group", "name": "Network", "tab": "457102eadc9ddb6c", "order": 2, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "70d0be671bf03ca7", "type": "ui_group", "name": "Pinout", "tab": "457102eadc9ddb6c", "order": 6, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "7a3279eea439bcdd", "type": "ui_group", "name": "Motor", "tab": "457102eadc9ddb6c", "order": 5, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "d324f0b852c2df0a", "type": "ui_group", "name": "Camera", "tab": "457102eadc9ddb6c", "order": 4, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "12b719cba49817c9", "type": "ui_group", "name": "OpenScanCloud", "tab": "457102eadc9ddb6c", "order": 3, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "457102eadc9ddb6c", "type": "ui_tab", "name": "Settings", "icon": "dashboard", "order": 4, "disabled": false, "hidden": false }, { "id": "6e339d87c7d5debe", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "db43d646.2074c8", "order": 1, "width": 1, "height": 1 }, { "id": "33b6d7317d1524b8", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "db43d646.2074c8", "order": 3, "width": 1, "height": 1 }, { "id": "aaf5b874c52a58aa", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "365a30d0dfa83e95", "order": 8, "width": 7, "height": 1 }, { "id": "2e08d4415665c939", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "365a30d0dfa83e95", "order": 9, "width": 1, "height": 1 }, { "id": "f8d8740dcbf499fb", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "365a30d0dfa83e95", "order": 11, "width": 1, "height": 1 }, { "id": "7ac0cb556740d159", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "365a30d0dfa83e95", "order": 13, "width": 1, "height": 1 }, { "id": "4de2414e29020c74", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "90223f7ddc082321", "order": 2, "width": 7, "height": 1 }, { "id": "ac8c60543cb04139", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "ac7409105cfecac6", "order": 3, "width": 7, "height": 1 }, { "id": "ce21673092264c38", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "8ab79a98e536e0d6", "order": 3, "width": 6, "height": 1 }, { "id": "3f7b77f8a1675d27", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "12b719cba49817c9", "order": 7, "width": 4, "height": 1 }, { "id": "0799b02d12fc3a14", "type": "ui_spacer", "z": "e43a27722b508115", "name": "spacer", "group": "7a3279eea439bcdd", "order": 25, "width": 6, "height": 1 }, { "id": "bc4e2c03859196c3", "type": "inject", "z": "e6f4d02efb300ea9", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 100, "y": 460, "wires": [ [ "949bafced17d66d6" ] ] }, { "id": "949bafced17d66d6", "type": "function", "z": "e6f4d02efb300ea9", "name": "enable", "func": "msg.flag = global.set('flag_pw',true)\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 460, "wires": [ [] ] }, { "id": "a1f0ed7d5a9d670e", "type": "inject", "z": "e6f4d02efb300ea9", "name": "", "props": [ { "p": "overwrite", "v": "false", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "0.1", "topic": "", "x": 110, "y": 60, "wires": [ [ "544d20f02215011a", "325314c1a24fe5b4", "7a4a49f7dbe04e88", "b1e2491c952f84c9", "fac6626127bba4f5", "bc2f0adaf72f97e9", "ac242724fe7605a6" ] ] }, { "id": "544d20f02215011a", "type": "function", "z": "e6f4d02efb300ea9", "name": "CREATE FACTORY DEFAULT", "func": "msg = { \n'overwrite':msg.overwrite,\n'settings':\n {\n 'advanced_settings':false,\n 'cam_awbg_blue':0,\n 'cam_awbg_red':0,\n 'cam_contrast':1,\n 'cam_cropx':0,\n 'cam_cropy':0,\n 'cam_delay_after':0,\n 'cam_delay_before':0,\n 'camera':'',\n 'cam_features' : false,\n 'cam_focus_min': 11.5,\n 'cam_focus_max': 11.5,\n 'cam_gain':1,\n 'cam_jpeg_quality':95,\n 'cam_rotation':270,\n 'cam_saturation':1,\n 'cam_stacksize':1,\n 'cam_shutter':50000,\n 'cam_timeout':800,\n 'cam_mask_threshold':45,\n 'cam_mask':true,\n 'hostname':'openscan',\n 'model':'',\n 'osc_credit':'',\n 'osc_limit_filesize':'',\n 'osc_limit_photos':'',\n 'osc_splitsize':200000000,\n// 'pin_extra_endstop': 19,\n 'pin_external': 25,\n 'pin_ringlight1': 24,\n 'pin_ringlight2': 24,\n \n 'pin_rotor_endstop': 17,\n 'pin_rotor_dir': 23,\n 'pin_rotor_enable': 22,\n 'pin_rotor_step': 27,\n 'rotor_acc': 0.5,\n 'rotor_accramp': 500,\n 'rotor_angle': 10,\n 'rotor_anglemax': 75,\n 'rotor_anglemin': -25,\n 'rotor_anglestart': 25,\n 'rotor_delay': 0.0002,\n 'rotor_dir': 1,\n 'rotor_stepsperrotation': 35200/2,\n 'rotor_endstop_angle': 0,\n 'rotor_endstop_enable': false,\n\n // 'pin_tt_endstop': 25,\n 'pin_tt_dir': 6,\n 'pin_tt_enable': 22,\n 'pin_tt_step': 16,\n 'tt_acc': 1,\n 'tt_accramp': 200,\n 'tt_angle': 90,\n 'tt_delay': 0.0001,\n 'tt_dir': 1,\n 'tt_stepsperrotation': 1600,\n\n 'pin_extra_dir': 21,\n 'pin_extra_step': 20,\n 'pin_extra_enable': 22,\n 'extra_acc': 1,\n 'extra_accramp': 200,\n 'extra_angle': 10,\n 'extra_delay': 0.0001,\n 'extra_dir': 1,\n 'extra_stepsperrotation': 3200,\n\n 'routine_photocount':50,\n 'routine_projectname':'default',\n 'smb':true,\n 'ssh':true,\n 'status_cloud':'ready',\n 'status_internal_cam':'--READY--',\n 'status_uploadprogress':'',\n 'terms':false,\n 'token':'',\n\n 'uploadprogress':'',\n 'update_type':'beta',\n 'update_auto':true,\n 'turntable_mode':false,\n 'diskspace_threshold':4000,\n 'updateable':false,\n 'cam_focuspeak':false,\n 'cam_histogram':false,\n 'routine_secondpass':true,\n 'cam_output_resolution':20000000,\n 'cam_preview_resolution':2000000,\n 'cam_output_downscale':false,\n 'cam_sharparea':false,\n 'cam_sharpness':100,\n}}\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 330, "y": 60, "wires": [ [ "c77552216a8bb781" ] ] }, { "id": "c77552216a8bb781", "type": "python3-function", "z": "e6f4d02efb300ea9", "name": "chk files", "func": "from os import listdir\n\nbasedir = '/home/pi/OpenScan/settings/'\n\nmsg['payload'] = ''\n\nfiles = listdir(basedir)\n\nfor i in msg['settings']:\n if msg['overwrite'] != True:\n if i not in files:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n else:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n\nmsg['payload'] = True\n\nreturn msg", "outputs": 1, "x": 540, "y": 60, "wires": [ [ "960912e90ba5b5bc" ] ] }, { "id": "960912e90ba5b5bc", "type": "link out", "z": "e6f4d02efb300ea9", "name": "started1s", "mode": "link", "links": [ "2f4c0f98.dee2", "397ab7f44b893c89", "65145c939b6647e2", "65b38bfeb3fee710", "6d1e12f51f9af0b6", "788fabff98c7973c", "9b2bc9849aee310b", "a1e14624058e74cd", "a67c18aaca2f5fa5", "bd80ec228fb9a86d", "cc9c4092edeb43cc", "d3fc91d87d5d5f62", "d7c1fb4c028b21a5", "e5f38b4a07a5e278", "f0b355967b33dfee", "d0104e0163745993", "5e7d5e4335d37794", "1dffb799fdf10cbc", "9fd259de91de1da1", "fd0258418489839d", "b4c843620c251c43", "3876d5cbd248592b", "a4c81754c148b86f", "2e9b29c70969cf01", "2477f81cddc8fa31", "29036b35dfd672c6", "592ec13d8f8923a9", "cb40b9341bd22a28", "d1efcd5fa9d25785", "da61581182b7299e" ], "x": 645, "y": 60, "wires": [] }, { "id": "325314c1a24fe5b4", "type": "python3-function", "z": "e6f4d02efb300ea9", "name": "create path", "func": "import os\n\npaths = ['/home/pi/OpenScan/scans/preview/','/home/pi/OpenScan/tmp2/']\n\n\nfor i in paths:\n if not os.path.isdir(i):\n os.mkdir(i)", "outputs": 1, "x": 270, "y": 100, "wires": [ [] ] }, { "id": "168d72a54504b327", "type": "inject", "z": "e6f4d02efb300ea9", "name": "5/0.1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.1", "crontab": "", "once": true, "onceDelay": "5", "topic": "", "payload": "", "payloadType": "str", "x": 100, "y": 380, "wires": [ [ "6c6ef2255a7d39e5" ] ] }, { "id": "6c6ef2255a7d39e5", "type": "link out", "z": "e6f4d02efb300ea9", "name": "repeat 5s/0.1s", "mode": "link", "links": [ "61990987acd0f263", "2415272f42ce468c", "6bf8344af427a6ba" ], "x": 205, "y": 380, "wires": [] }, { "id": "7a4a49f7dbe04e88", "type": "python3-function", "z": "e6f4d02efb300ea9", "name": "LED Status", "func": "from OpenScan import fade_led, check_hotspot_mode, load_int\n\npin = load_int(\"pin_ringlight1\")\npin2 = load_int(\"pin_ringlight2\")\n\nif check_hotspot_mode():\n msg['mode'] = True\n i=4\n j=30\nelse:\n msg['mode'] = False\n i=2\n j=30\n\nfor x in range (i):\n fade_led(pin,j, 50, True)\n #fade_led(pin2,j, 50, True)\n fade_led(pin,j, 50, False)\n #fade_led(pin2,j, 50, False)\n pass\nreturn msg", "outputs": 1, "x": 270, "y": 140, "wires": [ [ "eb1a2387a1eeea76" ] ] }, { "id": "b1e2491c952f84c9", "type": "function", "z": "e6f4d02efb300ea9", "name": "global", "func": "global.set('light', 0)\nglobal.set('state1', 0)\nglobal.set('network_ssid',\"\")\nglobal.set('network_password',\"\")\nglobal.set('network_country',\"\")\nglobal.set('flag_pw', true)\nglobal.set('flag',false)\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 320, "wires": [ [] ] }, { "id": "fac6626127bba4f5", "type": "function", "z": "e6f4d02efb300ea9", "name": "enable", "func": "msg.enabled = true\nmsg.payload = \"\"\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 280, "wires": [ [ "200d4b9951b6e066" ] ] }, { "id": "200d4b9951b6e066", "type": "link out", "z": "e6f4d02efb300ea9", "name": "enable", "mode": "link", "links": [ "8367cfa0bf5bc5df", "c8b93b42c720b9cf", "65518f3d4e3095e5" ], "x": 345, "y": 280, "wires": [] }, { "id": "bc2f0adaf72f97e9", "type": "python3-function", "z": "e6f4d02efb300ea9", "name": "CAM init", "func": "from OpenScan import camera\n\ncamera(\"/picam2_init\")\n\nmotor_enable_pin = 22\n\nimport RPi.GPIO as GPIO # import RPi.GPIO module\nGPIO.setmode(GPIO.BCM) # choose BCM or BOARD\nGPIO.setwarnings(False)\nGPIO.setup(22, GPIO.OUT) # set a port/pin as an output\nGPIO.output(22, 0) \n", "outputs": 1, "x": 260, "y": 180, "wires": [ [] ] }, { "id": "8def60b68e21e665", "type": "inject", "z": "e6f4d02efb300ea9", "name": "FACTORY DEFAULT", "props": [ { "p": "overwrite", "v": "true", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": "0.1", "topic": "", "x": 800, "y": 40, "wires": [ [ "544d20f02215011a" ] ] }, { "id": "eb1a2387a1eeea76", "type": "link out", "z": "e6f4d02efb300ea9", "name": "enable LED", "mode": "link", "links": [ "592ec13d8f8923a9", "5baf89a2682265f7" ], "x": 385, "y": 140, "wires": [] }, { "id": "0d8c6bc7887fb3c2", "type": "ui_template", "z": "e6f4d02efb300ea9", "group": "365a30d0dfa83e95", "name": "shutdown+background", "order": 14, "width": 7, "height": 1, "format": "\n", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "global", "className": "", "x": 580, "y": 140, "wires": [ [] ] }, { "id": "ac242724fe7605a6", "type": "python3-function", "z": "e6f4d02efb300ea9", "name": "rescue incomplete project", "func": "#if project has not been done properly, this is a way to rescue the file\n\nfrom os import system\nfrom os.path import isfile\nfrom time import strftime\nfrom OpenScan import load_str\n\nbasepath = '/home/pi/OpenScan/'\nzippath = basepath + 'tmp/tmp.zip'\nprojectname=load_str(\"routine_projectname\")\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\n\nif isfile(zippath):\n system('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')", "outputs": 1, "x": 310, "y": 220, "wires": [ [] ] }, { "id": "4468f691.103eb8", "type": "ui_button", "z": "e6f4d02efb300ea9", "name": "", "group": "729f9ea6e3513c9b", "order": 1, "width": 3, "height": 2, "passthru": false, "label": "SCAN", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "1", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 540, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "6560dd25.9e76c4", "type": "ui_button", "z": "e6f4d02efb300ea9", "name": "", "group": "729f9ea6e3513c9b", "order": 3, "width": 3, "height": 2, "passthru": false, "label": "Settings", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "3", "payloadType": "num", "topic": "", "topicType": "str", "x": 100, "y": 620, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "62cd5288.2805fc", "type": "ui_ui_control", "z": "e6f4d02efb300ea9", "name": "", "events": "all", "x": 280, "y": 540, "wires": [ [] ] }, { "id": "71e72293.91c6fc", "type": "ui_button", "z": "e6f4d02efb300ea9", "name": "", "group": "729f9ea6e3513c9b", "order": 2, "width": 3, "height": 2, "passthru": false, "label": "Files", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "2", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 580, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "e7306ef2.3b4df", "type": "ui_button", "z": "e6f4d02efb300ea9", "name": "", "group": "729f9ea6e3513c9b", "order": 4, "width": 3, "height": 2, "passthru": false, "label": "Update&Info", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "4", "payloadType": "num", "topic": "", "topicType": "str", "x": 110, "y": 660, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "8955d11554f55e63", "type": "ui_button", "z": "e6f4d02efb300ea9", "name": "", "group": "5b3e5aca21140e9a", "order": 1, "width": 6, "height": 3, "passthru": false, "label": "Install Updates", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "date", "topic": "", "topicType": "str", "x": 120, "y": 720, "wires": [ [ "1e7457ea9c2c5e09" ] ] }, { "id": "1e7457ea9c2c5e09", "type": "link out", "z": "e6f4d02efb300ea9", "name": "update", "mode": "link", "links": [ "39a502b38837273d" ], "x": 245, "y": 720, "wires": [] }, { "id": "245e4341d4fb611c", "type": "function", "z": "e6f4d02efb300ea9", "name": "pinmap_v2", "func": "msg = { \n'overwrite':true,\n'settings':{\n 'pin_rotor_endstop':27,\n 'pin_tt_endstop':5,\n 'pin_extra_endstop':26,\n 'pin_external':25,\n 'pin_ringlight1':24,\n 'pin_ringlight2':24,\n 'pin_rotor_dir':23,\n 'pin_rotor_enable':19,\n 'pin_rotor_step':22,\n 'pin_tt_dir':6,\n 'pin_tt_enable':19,\n 'pin_tt_step':16,\n 'pin_extra_dir':21,\n 'pin_extra_step':20,\n 'pin_extra_enable':19,\n 'extra_acc':1,\n 'extra_accramp':200,\n 'extra_angle':10,\n 'extra_delay':0.0001,\n 'extra_dir':1,\n 'extra_stepsperrotation':3200,\n}}\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 790, "y": 540, "wires": [ [ "627406f3611511dc" ] ] }, { "id": "627406f3611511dc", "type": "python3-function", "z": "e6f4d02efb300ea9", "name": "write", "func": "from os import listdir\n\nbasedir = '/home/pi/OpenScan/settings/'\n\nmsg['payload'] = ''\n\nfiles = listdir(basedir)\n\nfor i in msg['settings']:\n if msg['overwrite'] != True:\n if i not in files:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n else:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n\nmsg['payload'] = True\n\nreturn msg", "outputs": 1, "x": 930, "y": 540, "wires": [ [ "50eeb3e362f9027f" ] ] }, { "id": "88b1bddde110298a", "type": "inject", "z": "e6f4d02efb300ea9", "name": "", "props": [ { "p": "overwrite", "v": "false", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": "0.1", "topic": "", "x": 650, "y": 540, "wires": [ [ "245e4341d4fb611c" ] ] }, { "id": "50eeb3e362f9027f", "type": "link out", "z": "e6f4d02efb300ea9", "name": "started1s", "mode": "link", "links": [ "2f4c0f98.dee2", "397ab7f44b893c89", "65145c939b6647e2", "65b38bfeb3fee710", "6d1e12f51f9af0b6", "788fabff98c7973c", "9b2bc9849aee310b", "a1e14624058e74cd", "a67c18aaca2f5fa5", "bd80ec228fb9a86d", "cc9c4092edeb43cc", "d3fc91d87d5d5f62", "d7c1fb4c028b21a5", "e5f38b4a07a5e278", "f0b355967b33dfee", "d0104e0163745993", "5e7d5e4335d37794", "b4c843620c251c43", "3876d5cbd248592b", "a4c81754c148b86f", "2e9b29c70969cf01", "2477f81cddc8fa31", "29036b35dfd672c6", "592ec13d8f8923a9", "cb40b9341bd22a28", "d1efcd5fa9d25785", "da61581182b7299e" ], "x": 1015, "y": 540, "wires": [] }, { "id": "4f3121f158f06a61", "type": "python3-function", "z": "e6f4d02efb300ea9", "name": "motor run", "func": "from OpenScan import motorrun, load_int\nfrom time import sleep\n\nmotorrun('rotor',300,True,False)\n\n", "outputs": 1, "x": 860, "y": 580, "wires": [ [] ] }, { "id": "4a8a04b1e5dca8fe", "type": "inject", "z": "e6f4d02efb300ea9", "name": "run rotor till endstop", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 690, "y": 580, "wires": [ [ "4f3121f158f06a61" ] ] }, { "id": "c8167775e3401fad", "type": "ui_template", "z": "e6f4d02efb300ea9", "group": "729f9ea6e3513c9b", "name": "infotext", "order": 4, "width": 0, "height": 0, "format": "

What's new?

\n
    \n
  • speed improvement 2-3x
  • \n
  • currently tested on OpenScan Mini + IMX519 with RPi 4
  • \n
  • optimized toolpath
  • \n
  • more responsive user interface
  • \n
  • hotspot mode (when no wireless network available ssid: openscan pw: opensource
  • \n
  • preview features and sharpness
  • \n
  • partial background masking
  • \n
  • no more autofocus --> instead you can set a min and max focus distance
  • \n
\nnote, that this is still an early beta and there might be some unintended bugs. please reach out to info@openscan.eu if you run into any issues.", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 580, "y": 260, "wires": [ [] ] }, { "id": "6a3d9acbe097a3d2", "type": "function", "z": "481edaf6db5a7a54", "name": "loadI", "func": "var file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 120, "wires": [ [ "cb6ebdabaaf7d0da" ] ] }, { "id": "7ef6f1b5c67201fe", "type": "function", "z": "481edaf6db5a7a54", "name": "write", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\n\nvar file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 120, "wires": [ [] ] }, { "id": "86f7d1b2d763f6e2", "type": "function", "z": "481edaf6db5a7a54", "name": "loadF", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) / 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 160, "wires": [ [ "c8a3fde5206ce1ae" ] ] }, { "id": "fd799c931139764d", "type": "function", "z": "481edaf6db5a7a54", "name": "loadI", "func": "var file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 240, "wires": [ [ "87be854db758a9a6" ] ] }, { "id": "d5140d455122c49a", "type": "function", "z": "481edaf6db5a7a54", "name": "loadI", "func": "var file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 280, "wires": [ [ "9daea4bd57f7a00e" ] ] }, { "id": "194f3590dd4f6e3d", "type": "function", "z": "481edaf6db5a7a54", "name": "write", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\nvar file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 240, "wires": [ [] ] }, { "id": "2de69452e829d780", "type": "function", "z": "481edaf6db5a7a54", "name": "write", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\nvar file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 280, "wires": [ [] ] }, { "id": "58e565fea35cb667", "type": "ui_text_input", "z": "481edaf6db5a7a54", "name": "", "label": "", "tooltip": "", "group": "365a30d0dfa83e95", "order": 3, "width": 4, "height": 1, "passthru": true, "mode": "text", "delay": "0", "topic": "", "sendOnBlur": true, "className": "", "topicType": "str", "x": 320, "y": 80, "wires": [ [ "734ac3bff2df6837" ] ] }, { "id": "97170908e1f4ac55", "type": "function", "z": "481edaf6db5a7a54", "name": "msg", "func": "msg.payload=\"default\"\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 80, "wires": [ [ "58e565fea35cb667" ] ] }, { "id": "734ac3bff2df6837", "type": "function", "z": "481edaf6db5a7a54", "name": "write", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\n\nvar file = 'routine_projectname'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload).replace(/ /g, '_')\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 80, "wires": [ [] ] }, { "id": "1dffb799fdf10cbc", "type": "link in", "z": "481edaf6db5a7a54", "name": "start routine settings", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 55, "y": 80, "wires": [ [ "97170908e1f4ac55", "6a3d9acbe097a3d2", "86f7d1b2d763f6e2", "fd799c931139764d", "d5140d455122c49a" ] ] }, { "id": "a0156eaac7dd35e5", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "shutter", "func": "from time import time, sleep\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\nimport math\n\n\ncamera('/picam2_exposure?exposure=' + str(int(msg['payload']*1000)))\n\nreturn msg\n", "outputs": 1, "x": 510, "y": 200, "wires": [ [] ] }, { "id": "c7f5808d753480d4", "type": "inject", "z": "481edaf6db5a7a54", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "6", "topic": "", "payload": "", "payloadType": "date", "x": 170, "y": 200, "wires": [ [ "11f41a6030578ef4" ] ] }, { "id": "11f41a6030578ef4", "type": "function", "z": "481edaf6db5a7a54", "name": "loadF", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) / 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 310, "y": 200, "wires": [ [ "a0156eaac7dd35e5" ] ] }, { "id": "855cbcadef1163c5", "type": "function", "z": "481edaf6db5a7a54", "name": "enable", "func": "msg.light = global.get('light')\nmsg.state1 = global.get('state1')\nmsg.flag = global.get('flag')\n\n\nvar min = 1;\nvar max = 100000;\nvar random = Math.floor(Math.random() * (max - min + 1)) + min;\n\nvar formatted = random.toString().padStart(3, '0');\nmsg.payload=\"/tmp2/preview.jpg?ts=\" + Date.now().toString();\n\nif (global.get('flag_pw') == false){\n if (msg.flag == true){\n return msg\n }\n return \n}\nelse{\n return msg\n}\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 840, "wires": [ [ "d1b87196ae5373ed", "41e6a4649b6afbfb", "2fd24f8e8e9c08b7", "85a268108250ba88" ] ] }, { "id": "1a443e20a973d2f1", "type": "change", "z": "481edaf6db5a7a54", "name": "flag_pw true", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 630, "y": 760, "wires": [ [] ] }, { "id": "d1b87196ae5373ed", "type": "change", "z": "481edaf6db5a7a54", "name": "flag_pw false", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 430, "y": 760, "wires": [ [] ] }, { "id": "03d92601c62b79d4", "type": "inject", "z": "481edaf6db5a7a54", "name": "4s/0.5", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.1", "crontab": "", "once": true, "onceDelay": "4", "topic": "Repeat", "payload": "0.1", "payloadType": "str", "x": 100, "y": 840, "wires": [ [ "855cbcadef1163c5" ] ] }, { "id": "41e6a4649b6afbfb", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "Take Preview Shot", "func": "from time import time, sleep\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\n\nstatus = load_str('status_internal_cam')\n\n#return msg\n\nmsg['payload']=\"/tmp2/preview.jpg?ts=\"+str(int(time()))\n\nif msg['flag'] == True:\n return msg\n\n\n#if status!=\"--READY--\":\n# return msg\n\n#msg['preview'] = True\n\ncamera('/picam2_take_photo')\n\nreturn msg\n", "outputs": 1, "x": 450, "y": 800, "wires": [ [ "1a443e20a973d2f1", "296636b7467fc745" ] ] }, { "id": "85a268108250ba88", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "90223f7ddc082321", "name": "preview_arducam", "order": 1, "width": 7, "height": 9, "format": "\n\n
\n \n
\n \n
\n
\n \n \n \n
\n\n \n\n\n\n \n \n
\n \n \n \n \n \n \n
\n \n
\n \n\n\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 450, "y": 840, "wires": [ [ "417f653ca0dfdcfc", "180476141c2a44ad" ] ] }, { "id": "296636b7467fc745", "type": "link out", "z": "481edaf6db5a7a54", "name": "link out 1", "mode": "link", "links": [ "2c58a1a66c4a8c11" ], "x": 575, "y": 800, "wires": [] }, { "id": "417f653ca0dfdcfc", "type": "delay", "z": "481edaf6db5a7a54", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "allowrate": false, "outputs": 1, "x": 640, "y": 840, "wires": [ [ "e864254b18c23dd1" ] ] }, { "id": "e864254b18c23dd1", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "motorrun", "func": "from OpenScan import motorrun, load_int\n\nif 'payload' not in msg:\n return\n\nif msg['payload'] == \"up\":\n motorrun('rotor',load_int('rotor_angle'))\nif msg['payload'] == \"down\":\n motorrun('rotor',-load_int('rotor_angle'))\nif msg['payload'] == \"left\":\n motorrun('tt',load_int('tt_angle'))\nif msg['payload'] == \"right\":\n motorrun('tt',-load_int('tt_angle'))\n\n", "outputs": 1, "x": 780, "y": 840, "wires": [ [] ] }, { "id": "180476141c2a44ad", "type": "function", "z": "481edaf6db5a7a54", "name": "global", "func": "if (typeof msg.light !== \"undefined\"){\n global.set('light',msg.light)\n}\nif (typeof msg.state1 !== \"undefined\"){\n global.set('state1',msg.state1)\n}\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 880, "wires": [ [ "8cbdbfecbd12ef83" ] ] }, { "id": "1fe18f3b0b52aabd", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "LED", "func": "from OpenScan import ringlight\nfrom time import time\n\nstarttime = time()\n\nif 'light' in msg:\n val = msg['light']\n while time()-starttime<0.02:\n if val == 0:\n ringlight(1,False)\n ringlight(2,False)\n\n elif val == 1:\n ringlight(1,True)\n ringlight(2,True)\n\nreturn msg", "outputs": 1, "x": 870, "y": 880, "wires": [ [] ] }, { "id": "2fd24f8e8e9c08b7", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "load advanced", "func": "from OpenScan import load_bool\n\nif 'state1' in msg:\n if msg['state1'] == 0:\n msg['payload']={\"group\":{\"hide\":[\"Scan_advanced\"],\"show\":[]}}\n else:\n msg['payload']={\"group\":{\"hide\":[],\"show\":[\"Scan_advanced\"]}}\n return msg", "outputs": 1, "x": 440, "y": 720, "wires": [ [ "923be3b2b25224b4" ] ] }, { "id": "923be3b2b25224b4", "type": "ui_ui_control", "z": "481edaf6db5a7a54", "name": "change visibility", "events": "all", "x": 640, "y": 720, "wires": [ [] ] }, { "id": "c8a3fde5206ce1ae", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "365a30d0dfa83e95", "name": "shutter", "order": 4, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 310, "y": 160, "wires": [ [ "034ec9f59e50a361", "a0156eaac7dd35e5" ] ] }, { "id": "034ec9f59e50a361", "type": "function", "z": "481edaf6db5a7a54", "name": "rate", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\nvar file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload * 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n});", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 160, "wires": [ [] ] }, { "id": "87be854db758a9a6", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "365a30d0dfa83e95", "name": "Cropy", "order": 7, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 310, "y": 240, "wires": [ [ "194f3590dd4f6e3d" ] ] }, { "id": "9daea4bd57f7a00e", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "365a30d0dfa83e95", "name": "Cropx", "order": 6, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 310, "y": 280, "wires": [ [ "2de69452e829d780" ] ] }, { "id": "cb6ebdabaaf7d0da", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "365a30d0dfa83e95", "name": "Photos", "order": 5, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 320, "y": 120, "wires": [ [ "7ef6f1b5c67201fe" ] ] }, { "id": "82ecd3cd971cb7ea", "type": "ui_text", "z": "481edaf6db5a7a54", "group": "365a30d0dfa83e95", "order": 2, "width": 3, "height": 1, "name": "projectname", "label": "Projectname", "format": "", "layout": "row-left", "className": "", "x": 530, "y": 40, "wires": [] }, { "id": "ed2974731fb8a84e", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "ac7409105cfecac6", "name": "threshold", "order": 5, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 320, "y": 520, "wires": [ [ "06e1e19835a9816e" ] ] }, { "id": "8cbdbfecbd12ef83", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "led", "func": "from OpenScan import fade_led, ringlight, load_int\n\npin = load_int('pin_ringlight1')\n\n\nif 'light' in msg:\n val = msg['light']\n\n if val ==1:\n fade_led(pin,50, 100, True)\n\n else:\n fade_led(pin,50, 100, False)\n\nreturn msg", "outputs": 1, "x": 750, "y": 880, "wires": [ [ "1fe18f3b0b52aabd" ] ] }, { "id": "06e1e19835a9816e", "type": "function", "z": "481edaf6db5a7a54", "name": "write", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\nvar file = 'cam_mask_threshold'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 520, "wires": [ [] ] }, { "id": "2d5b1eb4380ae5a8", "type": "function", "z": "481edaf6db5a7a54", "name": "loadI", "func": "var file = 'cam_mask_threshold'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 520, "wires": [ [ "ed2974731fb8a84e" ] ] }, { "id": "7dd287f40385922f", "type": "ui_button", "z": "481edaf6db5a7a54", "name": "start ", "group": "365a30d0dfa83e95", "order": 10, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-play", "payload": "", "payloadType": "date", "topic": "enabled", "topicType": "str", "x": 130, "y": 1040, "wires": [ [ "33d94a04b96a2de0", "6d15f717d5a11002", "9a6b30a0175a8ecd" ] ] }, { "id": "579f2211199fd6ab", "type": "ui_button", "z": "481edaf6db5a7a54", "name": "stop", "group": "365a30d0dfa83e95", "order": 12, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-stop", "payload": "numberofphotos", "payloadType": "global", "topic": "", "topicType": "str", "x": 490, "y": 1100, "wires": [ [ "1787f08ed7070ddd", "c1c044f3c2139f68" ] ] }, { "id": "1787f08ed7070ddd", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "stop", "func": "from OpenScan import load_str, save\n\nstatus = load_str('status_internal_cam')\n\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\n return\n\nsave('status_internal_cam', 'Routine-stopping')", "outputs": 1, "x": 630, "y": 1100, "wires": [ [] ] }, { "id": "e9b13dfd9f8d3711", "type": "link out", "z": "481edaf6db5a7a54", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "c8b93b42c720b9cf" ], "x": 395, "y": 1000, "wires": [] }, { "id": "9654deebb668e012", "type": "inject", "z": "481edaf6db5a7a54", "name": "1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "", "payloadType": "date", "x": 290, "y": 1140, "wires": [ [ "c1c044f3c2139f68" ] ] }, { "id": "8367cfa0bf5bc5df", "type": "link in", "z": "481edaf6db5a7a54", "name": "start routine", "links": [ "200d4b9951b6e066", "8689e938.dd9e38", "e9b13dfd9f8d3711", "f20f2dbc.0f123", "fb13752beddee9f2" ], "x": 45, "y": 1040, "wires": [ [ "7dd287f40385922f" ] ] }, { "id": "fb13752beddee9f2", "type": "link out", "z": "481edaf6db5a7a54", "name": "", "mode": "link", "links": [ "2f4c0f98.dee2", "8367cfa0bf5bc5df", "b33d604c.5f1a6", "c8b93b42c720b9cf" ], "x": 525, "y": 1040, "wires": [] }, { "id": "33d94a04b96a2de0", "type": "function", "z": "481edaf6db5a7a54", "name": "enable", "func": "global.set('flag', false)\n\nvar file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nvar data = fs.readFileSync(filepath+file, 'utf8');\n\n\nif (data === 'no camera found' || data.substring(0,5) === 'Featu'){\n return\n}\n\nmsg.enabled = true\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1100, "wires": [ [ "579f2211199fd6ab" ] ] }, { "id": "c1c044f3c2139f68", "type": "function", "z": "481edaf6db5a7a54", "name": "msg", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 490, "y": 1140, "wires": [ [ "579f2211199fd6ab" ] ] }, { "id": "1daf9e3a5bd5ab48", "type": "function", "z": "481edaf6db5a7a54", "name": "msg", "func": "global.set('flag_pw', true)\nglobal.set('flag', false)\nmsg.enabled = true\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 1040, "wires": [ [ "fb13752beddee9f2" ] ] }, { "id": "6d15f717d5a11002", "type": "function", "z": "481edaf6db5a7a54", "name": "disable", "func": "msg.enabled = false\nmsg.payload = false\nglobal.set(\"flag\",true)\n\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 1000, "wires": [ [ "e9b13dfd9f8d3711" ] ] }, { "id": "9a6b30a0175a8ecd", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "Routine", "func": "from OpenScan import load_bool, load_str, load_int, load_float, motorrun, sort_spherical_coordinates_deg, create_coordinates, take_photo, save, \\\n load_bool, camera\nfrom time import sleep, strftime, time\nfrom subprocess import getoutput\n\nfrom zipfile import ZipFile, ZIP_DEFLATED\nfrom os import system\nfrom os.path import isfile, getsize\nimport math\nimport threading\nimport numpy as np\n\nif load_str(\"status_internal_cam\") == \"no camera found\" or load_str(\"status_internal_cam\")[:5] == \"Featu\":\n return\n\n#motorrun('rotor', 140, ES_enable=True, ES_start_state=True)\n#motorrun('rotor', 10)\n\n\n\nsave('status_internal_cam', 'Routine-preparing')\ncamera('/picam2_switch_mode?mode=1')\n\nsave('cam_sharparea', False)\nsave('cam_features', False)\n\n\nprojectname = load_str(\"routine_projectname\")\nangle_max = load_int('rotor_anglemax')\nangle_min = load_int('rotor_anglemin')\nif load_bool('rotor_enable_endstop'):\n angle_start = load_int('rotor_endstop_angle')\n motorrun('rotor',angle_start/abs(angle_start) * 130, True, False)\n\nelse:\n angle_start = load_int('rotor_anglestart')\n\n\nphotocount = load_int('routine_photocount')\n\nfocus_min = load_float('cam_focus_min')\nfocus_max = load_float('cam_focus_max')\nstacksize = load_int('cam_stacksize')\n\nif focus_min == focus_max:\n stacksize = 1\n\nfocuslist = []\nif stacksize == 1:\n steps = 3 + int(abs(focus_max-focus_min)*0.8)\nelse:\n steps = stacksize\n\nfor i in range (steps):\n focuslist.append(min(focus_min,focus_max) + i * abs(focus_max-focus_min)/(steps-1))\n\nmsg['focuslist'] = focuslist\nmsg['payload2'] = []\ncounter = 0\n\nbasepath = '/home/pi/OpenScan/'\ntemppath = basepath + 'tmp2/preview.jpg'\nzippath = basepath + 'tmp.zip'\n\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\n\nif isfile(zippath):\n system('rm ' + zippath)\nsleep(1)\n\ncoordinates = create_coordinates(angle_min, angle_max, photocount)\ncoordinates = sort_spherical_coordinates_deg(coordinates)\n\nmsg['payload'] = coordinates\n\nposition_last = (angle_start, 0)\n\nzip = ZipFile(zippath, \"a\", ZIP_DEFLATED, allowZip64=True)\n\nstarttime = time()\n\ndef photo(counter2):\n camera('/picam2_take_photo')\n returning[0] = focus(returning[0])\n zip.write(temppath, projectname + '_' + str(counter) + \".jpg\")\n\ndef stack_photo(i):\n \n camera('/picam2_take_photo')\n name = projectname + '_' + str(counter) + \"-\" + str(i) + \".jpg\"\n\n zip.write(temppath, name)\n \ndef stack_focus(i):\n sleep(load_float('cam_shutter')/1000000*2)\n if i < len(focuslist)-1:\n camera('/picam2_focus?focus=' + str(focuslist[i+1]))\n else:\n camera('/picam2_focus?focus=' + str(focuslist[0]))\n sleep(1.7)\n\ndef photo_stack():\n camera('/picam2_focus?focus=' + str(focuslist[0]))\n for i in range(len(focuslist)):\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n save('status_internal_cam', 'Routine-Photo ' + str(counter) + '/' + str(photocount) + \"-F\"+ str(i+1))\n \n focus_thread = threading.Thread(target=stack_focus, args=(i,))\n photo_thread = threading.Thread(target=stack_photo, args=(i,))\n \n focus_thread.start()\n photo_thread.start()\n \n focus_thread.join()\n photo_thread.join()\n\n\n\ndef move_motor():\n rotor_angle = position[0] - position_last[0]\n msg['payload2'].append(rotor_angle)\n #if abs(rotor_angle) > 180:\n # rotor_angle = -360 * rotor_angle / abs(rotor_angle) + rotor_angle\n tt_angle = position_last[1] - position[1]\n if tt_angle > 180:\n tt_angle -= 360\n elif tt_angle < -180:\n tt_angle += 360\n\n motorrun('tt',tt_angle)\n motorrun('rotor',rotor_angle)\n return\n\n # THE FOLLOWING DOES NOT WORK PROPERLY WITH THREADING ?!\n\n #tt_thread = threading.Thread(target=motorrun, args=('tt', tt_angle))\n #rotor_thread = threading.Thread(target=motorrun, args=('rotor', rotor_angle))\n #tt_thread.start()\n #rotor_thread.start()\n #tt_thread.join()\n #rotor_thread.join()\n\n\ncounter2 = 0\n\ndef check_diskspace():\n diskspace_threshold = load_int('diskspace_threshold')\n diskspace = getoutput('df -h / | awk \"{print $5}\"').split('\\n')[1]\n available = int(float(diskspace.replace(' ','').split('G')[2])*1000)\n if available < diskspace_threshold:\n save('status_internal_cam', 'Routine-stopping')\n return\n\ndef focus(i):\n f = focuslist[i]\n camera('/picam2_focus?focus=' + str(f))\n if i < len(focuslist) - 1:\n i += 1\n else:\n i = 0\n return i\n\n\nfor position in coordinates:\n counter += 1\n filepath = basepath + 'tmp/' + projectname + '_' + str(counter) + \".jpg\"\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n if counter < 6:\n ETA = ''\n\n \n save('status_internal_cam', 'Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\n if counter > 6:\n check_diskspace()\n\n move_motor()\n sleep(load_float(\"cam_delay_before\"))\n \n if stacksize ==1:\n returning = [counter2]\n photo(returning)\n counter2 = returning[0]\n\n else:\n photo_stack()\n\n sleep(load_float(\"cam_delay_after\"))\n ETA = '-ETA:' + str(int((photocount / counter - 1) * (time() - starttime))) + '/' + str(\n int(photocount / counter * (time() - starttime))) + 's'\n position_last = position\n\nzip.close()\ncamera('/picam2_switch_mode?mode=0')\n\nsave('status_internal_cam', 'Routine-done')\n\nmotorrun('rotor', -position_last[0] )\nmotorrun('tt', position_last[1])\n\nsave('status_internal_cam', '--READY--')\n\nsystem('mv ' + zippath + \" \" + basepath + \"scans/\" + projectcode + \".zip\")\n\nreturn msg\n", "outputs": 1, "x": 300, "y": 1040, "wires": [ [ "1daf9e3a5bd5ab48", "795c85ad4f109567" ] ] }, { "id": "afe47a9eaae6f67f", "type": "ui_text", "z": "481edaf6db5a7a54", "group": "365a30d0dfa83e95", "order": 1, "width": 7, "height": 1, "name": "", "label": "Current Status:", "format": " {{msg.payload}} ", "layout": "row-spread", "className": "", "x": 340, "y": 40, "wires": [] }, { "id": "8608517d0567d63f", "type": "function", "z": "481edaf6db5a7a54", "name": "loadS", "func": "var file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\n\nif (data === 'no camera found'){\n msg.color = 'red'\n}\n\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 40, "wires": [ [ "afe47a9eaae6f67f" ] ] }, { "id": "6bf8344af427a6ba", "type": "link in", "z": "481edaf6db5a7a54", "name": "start status", "links": [ "6c6ef2255a7d39e5" ], "x": 55, "y": 40, "wires": [ [ "8608517d0567d63f" ] ] }, { "id": "78cfe60013a1bea4", "type": "ui_switch", "z": "481edaf6db5a7a54", "name": "", "label": "Show Sharpness", "tooltip": "", "group": "ac7409105cfecac6", "order": 2, "width": 7, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 350, "y": 380, "wires": [ [ "9774e7ad3b506354" ] ] }, { "id": "9774e7ad3b506354", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "focus", "func": "from OpenScan import save\nsave('cam_sharparea',msg['payload'])\n\nif not 'flag' in msg:\n msg['flag'] = True\n msg['payload'] = False\n return msg", "outputs": 1, "x": 510, "y": 380, "wires": [ [ "f0af909f3e739b22" ] ] }, { "id": "39c744466a21735e", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "90223f7ddc082321", "name": "focus_min", "order": 3, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 990, "y": 40, "wires": [ [ "fa181d22775c2ce6" ] ] }, { "id": "61aab497fa50898e", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "90223f7ddc082321", "name": "focus_max", "order": 4, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 990, "y": 80, "wires": [ [ "c615034ea6b26174" ] ] }, { "id": "5e83b653850fa16e", "type": "ui_template", "z": "481edaf6db5a7a54", "group": "ac7409105cfecac6", "name": "stacksize", "order": 4, "width": 7, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 320, "y": 480, "wires": [ [ "237c2135cdad86ea" ] ] }, { "id": "dd7fb8791d34c751", "type": "function", "z": "481edaf6db5a7a54", "name": "enable", "func": "global.set('light', 1)\nmsg.light = 1\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 880, "wires": [ [ "180476141c2a44ad" ] ] }, { "id": "5baf89a2682265f7", "type": "link in", "z": "481edaf6db5a7a54", "name": "enable led", "links": [ "eb1a2387a1eeea76" ], "x": 145, "y": 880, "wires": [ [ "dd7fb8791d34c751" ] ] }, { "id": "6a26e8a7253d708c", "type": "function", "z": "481edaf6db5a7a54", "name": "loadF", "func": "var file = 'cam_focus_min'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 830, "y": 40, "wires": [ [ "39c744466a21735e" ] ] }, { "id": "35ad7e55833836c1", "type": "function", "z": "481edaf6db5a7a54", "name": "loadF", "func": "var file = 'cam_focus_max'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 830, "y": 80, "wires": [ [ "61aab497fa50898e" ] ] }, { "id": "9fd259de91de1da1", "type": "link in", "z": "481edaf6db5a7a54", "name": "start routine settings", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 735, "y": 40, "wires": [ [ "6a26e8a7253d708c", "35ad7e55833836c1" ] ] }, { "id": "fa181d22775c2ce6", "type": "function", "z": "481edaf6db5a7a54", "name": "rate", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\nvar file = 'cam_focus_min'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n return msg\n});\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1150, "y": 40, "wires": [ [ "ae5ee8787145906d" ] ] }, { "id": "c615034ea6b26174", "type": "function", "z": "481edaf6db5a7a54", "name": "rate", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\nvar file = 'cam_focus_max'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n return msg\n});", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1150, "y": 80, "wires": [ [ "ae5ee8787145906d" ] ] }, { "id": "ae5ee8787145906d", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "focus", "func": "from OpenScan import camera\ncamera('/picam2_focus?focus=' + str(msg['payload']))\n\nreturn msg", "outputs": 1, "x": 1290, "y": 60, "wires": [ [] ] }, { "id": "f0af909f3e739b22", "type": "ui_switch", "z": "481edaf6db5a7a54", "name": "", "label": "Show Features", "tooltip": "", "group": "ac7409105cfecac6", "order": 1, "width": 7, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 340, "y": 420, "wires": [ [ "710fc2dbb5ef0167" ] ] }, { "id": "710fc2dbb5ef0167", "type": "python3-function", "z": "481edaf6db5a7a54", "name": "focus", "func": "from OpenScan import save\nsave('cam_features',msg['payload'])\n\n\nif not 'flag' in msg:\n msg['flag'] = True\n msg['payload'] = False\n return msg", "outputs": 1, "x": 510, "y": 420, "wires": [ [ "78cfe60013a1bea4" ] ] }, { "id": "d93c2b67bcf23b9a", "type": "function", "z": "481edaf6db5a7a54", "name": "loadI", "func": "var file = 'cam_stacksize'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 480, "wires": [ [ "5e83b653850fa16e" ] ] }, { "id": "237c2135cdad86ea", "type": "function", "z": "481edaf6db5a7a54", "name": "write", "func": "const delay = 100; // 100 ms delay\n\nif (!context.timeout) {\n context.timeout = setTimeout(() => {\n node.send({ payload: msg.payload });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n} else {\n context.set(\"lastMessage\", msg.payload);\n clearTimeout(context.timeout);\n context.timeout = setTimeout(() => {\n node.send({ payload: context.get(\"lastMessage\") });\n clearTimeout(context.timeout);\n delete context.timeout;\n }, delay);\n}\nvar file = 'cam_stacksize'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 480, "wires": [ [] ] }, { "id": "fd0258418489839d", "type": "link in", "z": "481edaf6db5a7a54", "name": "start routine settings", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 95, "y": 480, "wires": [ [ "2d5b1eb4380ae5a8", "d93c2b67bcf23b9a" ] ] }, { "id": "c6f281351e11b58a", "type": "inject", "z": "481edaf6db5a7a54", "name": "", "props": [ { "p": "enabled", "v": "true", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 190, "y": 600, "wires": [ [ "ed2974731fb8a84e" ] ] }, { "id": "ca4ca7fae36d312d", "type": "inject", "z": "481edaf6db5a7a54", "name": "", "props": [ { "p": "enabled", "v": "false", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 190, "y": 640, "wires": [ [ "ed2974731fb8a84e" ] ] }, { "id": "c8b93b42c720b9cf", "type": "link in", "z": "481edaf6db5a7a54", "name": "sharpness/features", "links": [ "200d4b9951b6e066", "e9b13dfd9f8d3711", "fb13752beddee9f2" ], "x": 85, "y": 380, "wires": [ [ "78cfe60013a1bea4" ] ] }, { "id": "795c85ad4f109567", "type": "debug", "z": "481edaf6db5a7a54", "name": "debug 5", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 620, "y": 1000, "wires": [] }, { "id": "ea54fcc2.cfcc2", "type": "python3-function", "z": "80a3942785a26c29", "name": "get dirs", "func": "from glob import glob\nimport os\nfrom zipfile import ZipFile\nfrom datetime import datetime\nfrom PIL import Image\n\ndef set_stats(stat):\n try:\n with open(directory+set[:-4]+\"/\"+stat,\"r\") as file:\n stat=file.read()\n except:\n stat=\"\"\n return stat\n\ntable=[]\ndirectory=\"/home/pi/OpenScan/scans/\"\n\nfor d in glob(directory+\"*.zip\"):\n set=os.path.basename(d)\n\n try:\n with ZipFile(d, 'r') as f:\n photos = len(f.namelist())\n \n if not os.path.isfile(directory + 'preview/' + os.path.basename(d)[:-4]+'.jpg'):\n image = f.open(f.namelist()[int(photos/2)])\n img = Image.open(image)\n width, height = img.size\n width_factor = width/300\n height_factor = height/295\n if height_factor>=width_factor and height_factor > 1:\n new_size=(int(width/height_factor), int(height/height_factor))\n img = img.resize(new_size)\n elif height_factor 1:\n new_size=(int(width/width_factor),int(height/width_factor))\n img = img.resize(new_size)\n img.save(directory + 'preview/' + os.path.basename(d)[:-4] +'.jpg')\n list=[]\n for fi in f.filelist:\n list.append(f.getinfo(fi.filename).date_time)\n \n duration = str(datetime(*max(list)) - datetime(*min(list)))\n \n size = float(int(float(os.path.getsize(d))/100000))/10\n size_full= os.path.getsize(d)\n status=set_stats(\"status\")\n expiration=set_stats(\"expiration\")\n download=set_stats(\"download\")\n \n if len(download)!=0:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Download\":\"RESULT\",\n \"Size_full\":size_full,\n \"Duration\":duration,\n })\n else:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Size_full\":size_full,\n \"Duration\":duration,\n\n })\n except:\n pass\n\nmsg['payload']=table\nmsg['topic']=\"\"\nreturn msg", "outputs": 1, "x": 480, "y": 180, "wires": [ [ "f3662f8c7d3d7a2d", "01e4783e148c6698" ] ] }, { "id": "2f4c0f98.dee2", "type": "link in", "z": "80a3942785a26c29", "name": "filelist", "links": [ "50eeb3e362f9027f", "960912e90ba5b5bc", "a4f09e25.02569", "ed35109311335099", "fb13752beddee9f2" ], "x": 355, "y": 220, "wires": [ [ "ea54fcc2.cfcc2" ] ] }, { "id": "952ce286.4ffd4", "type": "ui_text", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "order": 4, "width": 6, "height": 1, "name": "Status", "label": "Status", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 250, "y": 60, "wires": [] }, { "id": "d4383424.7807c8", "type": "python3-function", "z": "80a3942785a26c29", "name": "upload", "func": "import os\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\nfrom subprocess import getoutput\n\nbasedir = '/home/pi/OpenScan/'\n\nif load_str(\"feedback_terms\")==\"False\":\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic'] = 'OpenScanCloud - Terms of Use'\n return None,msg\n\nmsg = msg['payload']\n\ndef upload(filelist, ulinks):\n pid = getoutput('pidof curl')\n if pid != \"\":\n os.system('kill ' + pid)\n\n i = 0\n for file in filelist:\n link = ulinks[i]\n save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\n cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\"' + file + '\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\n i = i+1\n os.system(cmd)\n\n########\nif not os.path.isfile(basedir + 'settings/token'):\n msg['flag'] = True\n save('status_cloud', 'please enter token first')\n return msg\nwith open(basedir + 'settings/token', 'r') as file:\n token = file.read().strip('\\n')\n\n########\nr = OpenScanCloud('getTokenInfo', {'token':token})\n\nif r.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n save('status_cloud', 'invalid/missing token')\n return None,msg\nelif r.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nmsg1 = r.json()\n\n########\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\n msg['flag'] = True\n save('status_cloud', 'limit(s) exceeded')\n return msg\n\n########\ntemp = OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\nif temp.status_code not in (200,401):\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nif temp.status_code != 401:\n temp = temp.json()\n if 'status' in temp:\n if temp['status'] != 'created':\n save('status_cloud','already exists')\n with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\n file.write(temp['status'])\n return msg\n#####\n\nmsg2={}\nmsg2['token'] = token\nmsg2['parts'] = 1\nmsg['partslist']=[]\n\n#######\nsize_to_split = load_int('osc_splitsize')\n\nif msg['Size_full'] > size_to_split:\n tempdir = basedir + 'tmp/split/'\n if os.path.isdir(tempdir):\n os.system('rm -r ' + tempdir)\n os.mkdir(tempdir)\n save('status_cloud', 'zipping files, please wait ...')\n cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\n os.system(cmd)\n save('status_cloud', 'zip done')\n list = os.listdir(tempdir)\n for l in list:\n msg['partslist'].append(tempdir + l)\n msg['partslist'].sort()\n msg2['parts']=len(msg['partslist'])\nelse:\n msg['partslist'] = [basedir + 'scans/' +msg['Set']]\n\n#######\nmsg2['photos'] = msg['Photos']\nmsg2['filesize'] = msg['Size_full']\nmsg2['project'] = msg['Set']\n\nr = OpenScanCloud('createProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nmsg1 = r.json()\n\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\n os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\n file.write('prepared')\n\nsave('status_cloud', 'uploading')\nupload(msg['partslist'], msg1['ulink'])\n\nr = OpenScanCloud('startProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Upload failed'\n msg['payload'] = 'please try again'\n save('status_cloud', 'upload failed')\n return None,msg\n\nsave('status_cloud', 'uploaded')\n\nsave('status_cloud', 'project started')\n\ntry:\n os.system('rm -r ' + tempdir)\nexcept:\n pass\n\nreturn msg", "outputs": 2, "x": 530, "y": 460, "wires": [ [ "9a132ab1.b21658" ], [ "3d16b3789632784d", "9a132ab1.b21658" ] ] }, { "id": "50710948.71c308", "type": "change", "z": "80a3942785a26c29", "name": "set", "rules": [ { "t": "set", "p": "set", "pt": "global", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 750, "y": 180, "wires": [ [ "ada1b6f7cccc9344", "85839a17fb7b58b9" ] ] }, { "id": "834046a4.647938", "type": "ui_text", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "order": 5, "width": 6, "height": 1, "name": "Set", "label": "Set:", "format": "{{msg.payload.Name}}", "layout": "row-spread", "className": "", "x": 750, "y": 220, "wires": [] }, { "id": "9a132ab1.b21658", "type": "change", "z": "80a3942785a26c29", "name": "flag.true", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 780, "y": 460, "wires": [ [ "8689e938.dd9e38" ] ] }, { "id": "3c67e97b.9d19a6", "type": "function", "z": "80a3942785a26c29", "name": "enable", "func": "//if (global.get('flag') === false){\n// msg.enabled = false\n// msg.color=\"white\"\n//}\n//else{\n\n msg.enabled = true\n msg.color=\"red\"\n \n//}\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 130, "y": 340, "wires": [ [ "7a93d1e18254685c", "e434ef42bd6b92e8", "d5d840183025d91b", "ab9e90ab5a53a0dd", "478994f671a3907d" ] ] }, { "id": "bfc01f26.c32cf", "type": "change", "z": "80a3942785a26c29", "name": "flag.false", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 420, "y": 500, "wires": [ [ "f20f2dbc.0f123" ] ] }, { "id": "b33d604c.5f1a6", "type": "link in", "z": "80a3942785a26c29", "name": "enable cloud", "links": [ "4082b136.dae18", "8689e938.dd9e38", "bd75f33b8a57c522", "e9b13dfd9f8d3711", "f20f2dbc.0f123", "fb13752beddee9f2" ], "x": 35, "y": 340, "wires": [ [ "3c67e97b.9d19a6" ] ] }, { "id": "f6bd1a04.470838", "type": "change", "z": "80a3942785a26c29", "name": "set", "rules": [ { "t": "set", "p": "payload", "pt": "msg", "to": "set", "tot": "global" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 410, "y": 460, "wires": [ [ "d4383424.7807c8" ] ] }, { "id": "4082b136.dae18", "type": "link out", "z": "80a3942785a26c29", "name": "", "links": [ "b33d604c.5f1a6", "87574a42938afec4" ], "x": 715, "y": 140, "wires": [] }, { "id": "f20f2dbc.0f123", "type": "link out", "z": "80a3942785a26c29", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 515, "y": 500, "wires": [] }, { "id": "8689e938.dd9e38", "type": "link out", "z": "80a3942785a26c29", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 875, "y": 460, "wires": [] }, { "id": "15de0ebb.616d61", "type": "ui_toast", "z": "80a3942785a26c29", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 380, "wires": [ [ "a7d89487.ee8858" ] ] }, { "id": "a7d89487.ee8858", "type": "python3-function", "z": "80a3942785a26c29", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\ntry:\n os.remove(dir+msg['Set'])\n shutil.rmtree(dir+msg['Set'][:-4])\nexcept:\n pass\nreturn msg", "outputs": 1, "x": 690, "y": 380, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "a4f09e25.02569", "type": "link out", "z": "80a3942785a26c29", "name": "", "mode": "link", "links": [ "2f4c0f98.dee2" ], "x": 775, "y": 360, "wires": [] }, { "id": "7a93d1e18254685c", "type": "link out", "z": "80a3942785a26c29", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "92c98e6ce7cd25f9" ], "x": 235, "y": 500, "wires": [] }, { "id": "4d99c601c9881680", "type": "python3-function", "z": "80a3942785a26c29", "name": "refresh", "func": "from time import sleep\nimport os\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\n\nbasepath = '/home/pi/OpenScan/scans/'\n\nif load_bool(\"terms\")==False:\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic']='OpenScanCloud - Terms of Use'\n return None,msg\n\nsave('status_cloud','refreshing')\ntoken = load_str('token')\n\ntest = OpenScanCloud('getTokenInfo',{'token':token})\nif test.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n return None,msg\nelif test.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nstats = test.json()\nfor i in stats:\n save('osc_'+i, stats[i])\n pass\n\nmsg={}\nprojects = []\nfor i in os.listdir(basepath):\n if i == 'preview':\n continue\n if os.path.isdir(basepath + i):\n if os.path.isfile(basepath + i + '/status'):\n with open(basepath + i + '/status', 'r') as file:\n status = file.read().strip('\\n')\n if status in ['expired', 'processing done', 'processing failed']:\n continue\n projects.append(i)\n\nfor p in projects:\n r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\n if r.status_code == 200:\n answer = r.json()\n if answer == {}:\n os.system('rm -r ' + basepath + p)\n else:\n with open(basepath + p + '/status', 'w+') as file:\n file.write(answer['status'])\n with open(basepath + p + '/download', 'w+') as file:\n file.write(answer['dlink'])\n\nmsg['list'] = projects\nsleep(0.5)\nsave('status_cloud','ready')\nreturn msg, None\n", "outputs": 2, "x": 320, "y": 180, "wires": [ [ "ea54fcc2.cfcc2", "b42e061fb1f1f3d7" ], [ "6434e713f088012b" ] ] }, { "id": "372e95797a3f2f3b", "type": "python3-function", "z": "80a3942785a26c29", "name": "limit :)", "func": "from time import sleep\n\nmsg2={}\nmsg2['enabled'] = True\n\nmsg['enabled'] = False\nnode.send(msg)\n\nwait = 15\n\nfor i in range (wait):\n msg['text'] = ' ('+ str(wait - i)+')'\n node.send(msg)\n\nmsg['enabled'] = True\nmsg['text']=\"\"\n\n\nreturn msg", "outputs": 1, "x": 90, "y": 220, "wires": [ [ "573edbfdb7500ddc" ] ] }, { "id": "573edbfdb7500ddc", "type": "delay", "z": "80a3942785a26c29", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 230, "y": 220, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "dacb1f078b624e10", "type": "ui_toast", "z": "80a3942785a26c29", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 340, "wires": [ [ "c8d65cc7c2ff7c36" ] ] }, { "id": "92c98e6ce7cd25f9", "type": "link in", "z": "80a3942785a26c29", "name": "", "links": [ "7a93d1e18254685c", "bd75f33b8a57c522" ], "x": 35, "y": 180, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "3d16b3789632784d", "type": "ui_toast", "z": "80a3942785a26c29", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 770, "y": 500, "wires": [ [] ] }, { "id": "6434e713f088012b", "type": "ui_toast", "z": "80a3942785a26c29", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 470, "y": 220, "wires": [ [] ] }, { "id": "c8d65cc7c2ff7c36", "type": "python3-function", "z": "80a3942785a26c29", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\nfor i in os.listdir(dir):\n if os.path.isdir(dir + i):\n shutil.rmtree(dir + i)\n else:\n os.remove(dir + i)\n\nos.mkdir(dir+'/preview')\nreturn msg", "outputs": 1, "x": 690, "y": 340, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "f4e9a4bd79b4221f", "type": "function", "z": "80a3942785a26c29", "name": "msg", "func": "msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 340, "wires": [ [ "dacb1f078b624e10" ] ] }, { "id": "2806bf08ea21216d", "type": "function", "z": "80a3942785a26c29", "name": "msg", "func": "msg.Set=global.get('set')['Set']\nmsg.payload = 'Are you sure to delete ' + msg.Set + '?'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 380, "wires": [ [ "15de0ebb.616d61" ] ] }, { "id": "61990987acd0f263", "type": "link in", "z": "80a3942785a26c29", "name": "", "links": [ "6c6ef2255a7d39e5" ], "x": 45, "y": 60, "wires": [ [ "51579603bce21e98" ] ] }, { "id": "e8e488a6dd5d0b33", "type": "ui_template", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "name": "Download", "order": 8, "width": 3, "height": 1, "format": "\n
Download\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 880, "y": 260, "wires": [ [] ] }, { "id": "0c387c0291d6c131", "type": "function", "z": "80a3942785a26c29", "name": "msg", "func": "msg.download = '/scans/' + String(msg.payload.Set)\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 750, "y": 260, "wires": [ [ "e8e488a6dd5d0b33" ] ] }, { "id": "e5f38b4a07a5e278", "type": "link in", "z": "80a3942785a26c29", "name": "", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 655, "y": 220, "wires": [ [ "834046a4.647938" ] ] }, { "id": "e434ef42bd6b92e8", "type": "ui_template", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "name": "upload2", "order": 7, "width": 3, "height": 1, "format": "upload", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 460, "wires": [ [ "f6bd1a04.470838" ] ] }, { "id": "c46e10b9c201913e", "type": "ui_template", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "name": "refresh", "order": 2, "width": 4, "height": 1, "format": "refresh{{msg.text}}", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 160, "y": 180, "wires": [ [ "372e95797a3f2f3b", "4d99c601c9881680" ] ] }, { "id": "d5d840183025d91b", "type": "ui_template", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "name": "del set", "order": 11, "width": 2, "height": 1, "format": "delete set", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 380, "wires": [ [ "2806bf08ea21216d" ] ] }, { "id": "ab9e90ab5a53a0dd", "type": "ui_template", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "name": "del ", "order": 10, "width": 2, "height": 1, "format": "delete all", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 340, "wires": [ [ "f4e9a4bd79b4221f" ] ] }, { "id": "478994f671a3907d", "type": "ui_template", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "name": "combine", "order": 9, "width": 2, "height": 1, "format": "combine", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 540, "wires": [ [ "51bfd0fb7b1d292e" ] ] }, { "id": "189c1eed09624a7b", "type": "function", "z": "80a3942785a26c29", "name": "combine", "func": "combine = global.get('combine')\ncombine_set = global.get('set').Set\n\nif (combine === true && global.get('combine_set') !== combine_set){\n msg.set1 = global.get('combine_set')\n msg.set2 = combine_set\n global.set('combine', false)\n msg.topic = 'Combine the following two sets:'\n msg.payload = msg.set1 + '
' + msg.set2 + '
FILES WILL BE MERGED INTO ON FILE!'\n return msg\n}\nglobal.set('combine_set' , combine_set)\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 580, "wires": [ [ "1493398979a63775" ] ] }, { "id": "51bfd0fb7b1d292e", "type": "function", "z": "80a3942785a26c29", "name": "combine", "func": "global.set('combine', true)\ncombine_set = global.get('set').Set\nmsg.topic = 'Merge two sets into one (can not be undone)!'\nmsg.payload = combine_set\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 420, "y": 540, "wires": [ [] ] }, { "id": "da325be8e74179be", "type": "python3-function", "z": "80a3942785a26c29", "name": "combine", "func": "from os.path import getsize\nfrom shutil import copy\nfrom os import rename, remove\nimport zipfile as z\nfrom OpenScan import save\n\nfrom time import sleep\n\nif msg['payload'] != 'OK':\n return\n\nbasepath = '/home/pi/OpenScan/scans/'\ntmp1 = basepath + msg['set1']\ntmp2 = basepath + msg['set2']\n\nif getsize(tmp1) > getsize(tmp2):\n set1 = tmp1\n set2 = tmp2\nelse:\n set1 = tmp2\n set2 = tmp1\n\nzips = [set1, set2]\n\nwith z.ZipFile(set1, 'a') as z1:\n z2 = z.ZipFile(set2, 'r')\n i = 0\n for n in z2.namelist():\n i += 1\n n2 = n\n save('status_cloud','writing ' + str(i) + '/' + str(len(z2.namelist())))\n while 'X'+n in z1.namelist():\n n = 'X' + n\n z1.writestr('X'+n, z2.open(n2).read())\nsave('status_cloud','ready')\n\nos.rename(set1, set1[:-4] + 'X.zip')\nos.remove(set2)\n\nreturn msg", "outputs": 1, "x": 560, "y": 580, "wires": [ [ "ed35109311335099" ] ] }, { "id": "ed35109311335099", "type": "link out", "z": "80a3942785a26c29", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "2f4c0f98.dee2" ], "x": 655, "y": 580, "wires": [] }, { "id": "1493398979a63775", "type": "ui_toast", "z": "80a3942785a26c29", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "Combine", "x": 420, "y": 580, "wires": [ [ "da325be8e74179be" ] ] }, { "id": "ada1b6f7cccc9344", "type": "link out", "z": "80a3942785a26c29", "name": "combine", "mode": "link", "links": [ "6dd356510c446cf4" ], "x": 835, "y": 180, "wires": [] }, { "id": "6dd356510c446cf4", "type": "link in", "z": "80a3942785a26c29", "name": "combine", "links": [ "ada1b6f7cccc9344" ], "x": 175, "y": 580, "wires": [ [ "189c1eed09624a7b" ] ] }, { "id": "b42e061fb1f1f3d7", "type": "link out", "z": "80a3942785a26c29", "name": "", "mode": "link", "links": [ "397ab7f44b893c89", "3876d5cbd248592b" ], "x": 435, "y": 140, "wires": [] }, { "id": "b99505440832439f", "type": "python3-function", "z": "80a3942785a26c29", "name": "diskspace", "func": "from subprocess import getoutput\nfrom OpenScan import load_int\n\ndiskspace_threshold = load_int('diskspace_threshold')\n\ndiskspace = getoutput('df -h / | awk \"{print $5}\"').split('\\n')[1]\n\navailable = int(float(diskspace.replace(' ','').split('G')[2])*1000)\n\n\nif available < diskspace_threshold:\n msg['topic'] = 'Low diskspace remaining! ('+str(available)+'MB)' \n msg['payload'] = 'Please delete some/all locally stored files.'\n msg['color'] = 'red'\n return msg\n", "outputs": 1, "x": 800, "y": 100, "wires": [ [ "92047434f8e9f927" ] ] }, { "id": "92047434f8e9f927", "type": "ui_toast", "z": "80a3942785a26c29", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 950, "y": 100, "wires": [ [] ] }, { "id": "f3662f8c7d3d7a2d", "type": "delay", "z": "80a3942785a26c29", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "minute", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "allowrate": false, "outputs": 1, "x": 650, "y": 100, "wires": [ [ "b99505440832439f" ] ] }, { "id": "51579603bce21e98", "type": "python3-function", "z": "80a3942785a26c29", "name": "read", "func": "from OpenScan import load_str\nfrom os import listdir, path\n\nstatus = load_str('status_cloud')\n\nif status[0:9] == 'uploading':\n progress = load_str('status_uploadprogress')[-6:]\n if progress[-1:] == '%':\n status = status + ' (' + progress + ')'\n\nif status[0:7] == 'zipping':\n path1 = '/home/pi/OpenScan/tmp/split/'\n files = listdir(path1)\n size1 = 0\n for file in files:\n size1 += path.getsize(path1+file)\n size2 = path.getsize('/home/pi/OpenScan/scans/'+ files[0][:-2])\n \n status = 'zipping files (' + str(float(int(1000*size1/size2))/10) + '%)'\n\nmsg['status'] = status\nreturn msg\n", "outputs": 1, "x": 130, "y": 60, "wires": [ [ "952ce286.4ffd4" ] ] }, { "id": "9a5baae623355f9d", "type": "ui_template", "z": "80a3942785a26c29", "group": "db43d646.2074c8", "name": "preview", "order": 6, "width": 6, "height": 6, "format": "
\n\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 1020, "y": 220, "wires": [ [] ] }, { "id": "85839a17fb7b58b9", "type": "python3-function", "z": "80a3942785a26c29", "name": "preview", "func": "from time import time\nimport os\n\npath = '/home/pi/OpenScan/scans/preview/'\nimage = os.path.basename(msg['payload']['Set'])[:-4] +'.jpg'\n\nmsg['payload']=\"/scans/preview/\" + image +\"?ts=\"+str(int(time()*10))\nreturn msg", "outputs": 1, "x": 880, "y": 220, "wires": [ [ "9a5baae623355f9d" ] ] }, { "id": "50020809fbd23673", "type": "inject", "z": "80a3942785a26c29", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "5", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 310, "y": 260, "wires": [ [ "ea54fcc2.cfcc2" ] ] }, { "id": "01e4783e148c6698", "type": "ui_table", "z": "80a3942785a26c29", "group": "b5fdd57b.15eda8", "name": "", "order": 1, "width": 13, "height": 7, "columns": [ { "field": "Date", "title": "Date", "width": "150", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Name", "title": "Name", "width": "150", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Photos", "title": "Photos", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Duration", "title": "ΔT", "width": "60", "align": "left", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Size", "title": "Size", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Status", "title": "Status", "width": "140", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } } ], "outputs": 1, "cts": true, "x": 610, "y": 180, "wires": [ [ "4082b136.dae18", "50710948.71c308", "834046a4.647938", "0c387c0291d6c131" ] ] }, { "id": "cb3437ec113e1b6f", "type": "ui_switch", "z": "e43a27722b508115", "name": "", "label": "SSH", "tooltip": "", "group": "4390b2ebcbbe104c", "order": 3, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 390, "y": 360, "wires": [ [ "c24f61b87e3226dd" ] ] }, { "id": "60fd0adce1cfeb82", "type": "ui_switch", "z": "e43a27722b508115", "name": "", "label": "Samba", "tooltip": "", "group": "4390b2ebcbbe104c", "order": 4, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "test2", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 400, "y": 400, "wires": [ [ "441d3ef525e901da" ] ] }, { "id": "c24f61b87e3226dd", "type": "python3-function", "z": "e43a27722b508115", "name": "ssh", "func": "from OpenScan import load_bool, save\n\nstate = msg['payload']\n\nif state != load_bool('ssh'):\n save('ssh', state)\n\nif state == True:\n os.system('/etc/init.d/ssh start')\nelse:\n os.system('/etc/init.d/ssh stop')", "outputs": 1, "x": 530, "y": 360, "wires": [ [] ] }, { "id": "c013e836e759a085", "type": "ui_button", "z": "e43a27722b508115", "name": "", "group": "4390b2ebcbbe104c", "order": 2, "width": 6, "height": 1, "passthru": false, "label": "Terms Of Use", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 120, "y": 320, "wires": [ [ "b78346ca3ce70c68" ] ] }, { "id": "f0d8dbcca76a1926", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Agree", "cancel": "Disagree", "raw": true, "className": "", "topic": "", "name": "", "x": 410, "y": 320, "wires": [ [ "e95b86cbac1b03b9" ] ] }, { "id": "34374044c0030625", "type": "ui_button", "z": "e43a27722b508115", "name": "General", "group": "4390b2ebcbbe104c", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

General Settings

Terms Of Use

In order to use the OpenScanCloud, please read the terms of use as files will be transmitted from your device to the OpenScan Servers.

SSH

SSH can be used to access the Raspberry Pi and modify core files of the operating system. Please deactivate, if you do not want to use this feature.

If you want to use it, the default user is pi, password: raspberry. Please change the password immediately. 

Samba

Samba s a network local file sharing server, which allows accessing the Raspberry Pi's file system through the explorer (and other programs like FileZilla). You can use it to transfer custom photo sets to the device in order to use the OpenScanCloud. Therefore, you need to transfer the zip file containing your photos to the following folder /OpenScan/scans/

You can access the Raspberry Pis file system by inserting the following line into your Windows explorer: 

\\\\OpenScan/PiShare/OpenScan/scans/

username: pi, password: raspberry

Please deactivate the local file sharing if you do not intend to use it

Advanced Settings

Enable a ton of additional settings, which should be changed only if you know what you are doing ;)

Model

Device model you are using: OpenScan Mini or OpenScan Classic. Setting the device affects the settings of the motor (gear ratio, acceleration, speed). You can change those values manually in the advanced settings.

Camera

A wide range of camera modules is supported (Pi camera v1.3, v2.1, HQ, Arducam IMX519, IMX290, IMX378, OV9281). If you encounter any issues with those models, please check the orientation of the camera ribbon cable and its connectors.

DSLR (gphoto) - connect a wide range of DSLR cameras to the device through USB. See GPhoto for a full list of supported devices.

External camera - triggering any camera through an isolated GPIO signal on the front side of the pi shield.

Shutdown/Reboot

Always use the shutdown button before you power off your Raspberry Pi.

Restore Default Settings

In case you want to restore the default settings

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 740, "y": 220, "wires": [ [ "5fff689f9f8bc1ca" ] ] }, { "id": "b2b6bf23c9989133", "type": "ui_button", "z": "e43a27722b508115", "name": "Pinout", "group": "70d0be671bf03ca7", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Pinout

ONLY CHANGE THE PINOUT IF YOU ARE ABSOLUTELY SURE! CHANGES CAN DAMAGE THE RASPBERRY PI AND ANY PERIPHERALS!


", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 430, "y": 220, "wires": [ [ "5fff689f9f8bc1ca" ] ] }, { "id": "441d3ef525e901da", "type": "python3-function", "z": "e43a27722b508115", "name": "smb", "func": "from OpenScan import load_bool, save\n\nstate = msg['payload']\n\nif state != load_bool('smb'):\n save('smb', state)\nif state == True:\n os.system('/etc/init.d/smbd start')\nelse:\n os.system('/etc/init.d/smbd stop')\n\n\n", "outputs": 1, "x": 530, "y": 400, "wires": [ [] ] }, { "id": "3256bab150113a48", "type": "ui_button", "z": "e43a27722b508115", "name": "Motor", "group": "7a3279eea439bcdd", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Motor Settings

Turntable Mode

Activate turntable mode in order to deactivate the rotor. The routine will only move the turntable and take a given number of photos.

Rotor - Start Angle, Min and Max Angle

Since this version of OpenScan does not have an endstop (yet), it is necessary to tell the device its position when the routine is being started. 0° corresponds to the horizontal (natural) orientation.

After that, the device will equally space the image positions between angle min and angle max.

Rotor/Turntable

Steps per rotation -  defines the number of steps it takes to move the axis 360°. It is defined by A*B*C, where A is the number of steps for one revolution of the given stepper motor (normally 200), B is the microstepping used (normally 16), and C the gear ratio (1 for the turntable and 15 or 5,33 for the OpenScan Mini and Classic respectively)

Delay - time in microseconds between each step of the motor. Lower this value if the movement is too fast

Acceleration - a factor defining how fast the delay time between each step is being changed during acceleration and deceleration phases. Lower this value in order to make the movement smoother.

Acceleration ramp - the number of steps allowed for the acceleration processes. Increase this value, if you want smoother movement.

Manual Angle - Defines the degree value for the manual movement through the arrow buttons in the scan menu

Direction - If needed, reverse the movement (in case the arrow buttons and movement do not correspond). Alternatively, you can flip the motor cable 180° (BUT MAKE SURE TO POWER OFF THE DEVICE!)


", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 430, "y": 140, "wires": [ [ "5fff689f9f8bc1ca" ] ] }, { "id": "7a186669a17daa71", "type": "ui_button", "z": "e43a27722b508115", "name": "camera", "group": "d324f0b852c2df0a", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Camera Settings

Jpeg quality

Value in percent, which usually does not need to be changed.

Downscale Preview

The preview image has to be scaled down depending on your network speed. If you want to have a higher quality preview image, you can increase this value, which defines the maximal width/height value. If the value is too high, the preview window might not update

Image Rotation

Change the image rotation, if needed.

Timeout

Defines the time in seconds, when the libcamera command (used for the camera modules) will timeout. Increase this value, if the camera does not get triggered in each position.

Delay Before/After

A fixed delay in seconds before and/or after a photo is taken. Increase this value when the photos have visual motion blur.

AWBG, Gain, Contrast, Saturation

Under most circumstances, you do not need to touch these values.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 420, "y": 180, "wires": [ [ "5fff689f9f8bc1ca" ] ] }, { "id": "edac7dd292e7e486", "type": "comment", "z": "e43a27722b508115", "name": "General Settings", "info": "", "x": 120, "y": 280, "wires": [] }, { "id": "161b52034e578ee2", "type": "comment", "z": "e43a27722b508115", "name": "Network", "info": "", "x": 100, "y": 720, "wires": [] }, { "id": "f6d6cc35679ede63", "type": "ui_switch", "z": "e43a27722b508115", "name": "more sets", "label": "Advanced Settings", "tooltip": "", "group": "4390b2ebcbbe104c", "order": 5, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 400, "y": 460, "wires": [ [ "f06a7bcad524e9f9" ] ] }, { "id": "29745a36fc157f3f", "type": "python3-function", "z": "e43a27722b508115", "name": "more sets", "func": "from OpenScan import save\n\nif msg['payload'] != 'OK':\n msg['payload'] = False\n return None,msg\n \nsave('advanced_settings', True)\n\nreturn msg", "outputs": 2, "x": 820, "y": 460, "wires": [ [ "8750ad979e9ea246" ], [ "f6d6cc35679ede63" ] ] }, { "id": "bf23328f9fb11b22", "type": "ui_ui_control", "z": "e43a27722b508115", "name": "change visibility", "events": "all", "x": 600, "y": 60, "wires": [ [] ] }, { "id": "b37be1d222bc70c9", "type": "inject", "z": "e43a27722b508115", "name": "1s_repeater", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "1", "crontab": "", "once": true, "onceDelay": "2", "topic": "", "payload": "", "payloadType": "date", "x": 150, "y": 60, "wires": [ [ "89eedf29b404f750" ] ] }, { "id": "89eedf29b404f750", "type": "python3-function", "z": "e43a27722b508115", "name": "load advanced", "func": "from OpenScan import load_bool\n\nif load_bool('advanced_settings') == False:\n msg['payload']={\"group\":{\"hide\":[\"Settings_Camera\",\"Settings_Motor\",\"Settings_Pinout\",],\"show\":[\"Settings_General\",\"Settings_Network\",\"Settings_OpenScanCloud\"]}}\nelse:\n msg['payload']={\"group\":{\"hide\":[],\"show\":[\"Settings_General\",\"Settings_Network\",\"Settings_OpenScanCloud\",\"Settings_Camera\",\"Settings_Motor\",\"Settings_Pinout\",]}}\n\nupdate = load_bool('updateable')\n\nmsg2 = {}\n\nif update == True:\n msg2['payload'] = {\"group\":{\"show\":[\"OpenScan_Update\"]}}\nelif update == False:\n msg2['payload'] = {\"group\":{\"hide\":[\"OpenScan_Update\"]}}\n\n\nreturn msg,msg2", "outputs": 2, "x": 360, "y": 60, "wires": [ [ "bf23328f9fb11b22" ], [ "bf23328f9fb11b22" ] ] }, { "id": "2050de5d9e02f69f", "type": "comment", "z": "e43a27722b508115", "name": "Info Texts", "info": "", "x": 100, "y": 140, "wires": [] }, { "id": "ded3086945a6d4b5", "type": "python3-function", "z": "e43a27722b508115", "name": "check ip address", "func": "import socket\nimport subprocess\n\ntestIP = \"8.8.8.8\"\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\ns.connect((testIP, 0))\nipaddr = s.getsockname()[0]\nhost = socket.gethostname()\n\nmsg['ip']=ipaddr\n\nreturn msg", "outputs": 1, "x": 250, "y": 940, "wires": [ [ "3cfe464506f46ecd" ] ] }, { "id": "3cfe464506f46ecd", "type": "ui_text", "z": "e43a27722b508115", "group": "8ab79a98e536e0d6", "order": 1, "width": 0, "height": 0, "name": "", "label": "Your local IP:", "format": "{{msg.ip}}", "layout": "row-spread", "className": "", "x": 430, "y": 940, "wires": [] }, { "id": "bd206ad109831e6a", "type": "comment", "z": "e43a27722b508115", "name": "OpenScanCloud", "info": "", "x": 120, "y": 1260, "wires": [] }, { "id": "b70a9a665c1e4d36", "type": "ui_button", "z": "e43a27722b508115", "name": "Cloud-settings", "group": "12b719cba49817c9", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

OpenScanCloud

OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at BuyMeACoffee.

The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.

Register

In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.

Enter Token

Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact cloud@openscan.eu

Token

A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests cloud@openscan.eu

Credit (GB)

Each token comes with a given amount of 'credit' which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers. 

IMPORTANT: The credit can be increased at any time by sending a (nice) mail to cloud@openscan.eu

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 580, "y": 200, "wires": [ [ "5fff689f9f8bc1ca" ] ] }, { "id": "c9f0566601a3e130", "type": "ui_text", "z": "e43a27722b508115", "group": "12b719cba49817c9", "order": 4, "width": 0, "height": 0, "name": "", "label": "Max. Number of Photos:", "format": "{{msg.limit_photos}}", "layout": "row-spread", "className": "", "x": 410, "y": 1400, "wires": [] }, { "id": "9bd86d27ea499a2a", "type": "ui_text", "z": "e43a27722b508115", "group": "12b719cba49817c9", "order": 5, "width": 0, "height": 0, "name": "", "label": "Max. Filesize (GB):", "format": "{{msg.limit_filesize}}", "layout": "row-spread", "className": "", "x": 390, "y": 1440, "wires": [] }, { "id": "2c37f7030810d234", "type": "ui_text", "z": "e43a27722b508115", "group": "12b719cba49817c9", "order": 3, "width": 0, "height": 0, "name": "", "label": "Credit (GB):", "format": "{{msg.credit}}", "layout": "row-spread", "className": "", "x": 370, "y": 1480, "wires": [] }, { "id": "f40286c18afd4501", "type": "python3-function", "z": "e43a27722b508115", "name": "save", "func": "import requests\nimport os\nfrom OpenScan import save, OpenScanCloud\n\nif msg['payload']!=\"Yes\":\n return None,msg\n\ntry:\n r = OpenScanCloud('getTokenInfo', {'token':msg['token']})\n if r.status_code != 200:\n msg['payload'] = 'Could not verify token'\n return msg \n \n msg1 = r.json()\n \n save('osc_credit',msg1['credit'])\n save('osc_limit_filesize',msg1['limit_filesize'])\n save('osc_limit_photos',msg1['limit_photos'])\n msg1['enabled'] = True\nexcept:\n pass\n\nsave('token',msg['token'])\n \nmsg['payload'] = 'Token verified and saved'\nreturn msg, msg1", "outputs": 2, "x": 750, "y": 1340, "wires": [ [ "455a5266017ea121", "50f73cee213ec05c" ], [ "264eece408043021" ] ] }, { "id": "455a5266017ea121", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "topic": "", "name": "", "x": 890, "y": 1300, "wires": [ [] ] }, { "id": "c368df68593bc2bf", "type": "ui_text_input", "z": "e43a27722b508115", "name": "", "label": "Token", "tooltip": "", "group": "12b719cba49817c9", "order": 2, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "", "sendOnBlur": true, "className": "", "topicType": "str", "x": 350, "y": 1360, "wires": [ [ "18fd1afa768187b3" ] ] }, { "id": "18fd1afa768187b3", "type": "python3-function", "z": "e43a27722b508115", "name": "Save?", "func": "msg['token'] = msg['payload']\n\nif len(msg['payload'])>=14:\n \n msg[\"payload\"]='Save and verify token: ' + msg['payload']\n return msg\nelse:\n return None,msg", "outputs": 2, "x": 470, "y": 1360, "wires": [ [ "418aea2ec65573a0" ], [ "9792c89c5f4429f9" ] ] }, { "id": "f90a98899b7a71d0", "type": "python3-function", "z": "e43a27722b508115", "name": "text", "func": "from OpenScan import load_str\n\ntoken = load_str('token')[0:8]\nmsg['payload']= token + '...'\nif len(token)==0:\n msg['payload']=\"enter token\"\nreturn msg", "outputs": 1, "x": 230, "y": 1360, "wires": [ [ "c368df68593bc2bf" ] ] }, { "id": "b4c843620c251c43", "type": "link in", "z": "e43a27722b508115", "name": "token", "links": [ "960912e90ba5b5bc", "50f73cee213ec05c", "9792c89c5f4429f9", "50eeb3e362f9027f" ], "x": 75, "y": 1360, "wires": [ [ "f90a98899b7a71d0" ] ] }, { "id": "418aea2ec65573a0", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 610, "y": 1340, "wires": [ [ "f40286c18afd4501" ] ] }, { "id": "9792c89c5f4429f9", "type": "link out", "z": "e43a27722b508115", "name": "", "mode": "link", "links": [ "b4c843620c251c43" ], "x": 555, "y": 1380, "wires": [] }, { "id": "264eece408043021", "type": "link out", "z": "e43a27722b508115", "name": "", "links": [ "5d267acc10020091", "3876d5cbd248592b" ], "x": 835, "y": 1380, "wires": [] }, { "id": "3876d5cbd248592b", "type": "link in", "z": "e43a27722b508115", "name": "OSCparameters", "links": [ "960912e90ba5b5bc", "264eece408043021", "b42e061fb1f1f3d7", "50eeb3e362f9027f" ], "x": 75, "y": 1400, "wires": [ [ "5daca3ec47f8e7fc" ] ] }, { "id": "50f73cee213ec05c", "type": "link out", "z": "e43a27722b508115", "name": "", "links": [ "b4c843620c251c43", "5d267acc10020091" ], "x": 835, "y": 1340, "wires": [] }, { "id": "95578e54a9b61cba", "type": "ui_toast", "z": "e43a27722b508115", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 250, "y": 1540, "wires": [ [ "d7a5693da7855da8" ] ] }, { "id": "d7a5693da7855da8", "type": "python3-function", "z": "e43a27722b508115", "name": "msg", "func": "import re\n\nif msg['payload'] == 'Cancel':\n return\n\nmail = msg['payload']\nemail_regex = re.compile(r\"[^@]+@[^@]+\\.[^@]+\")\n\nif email_regex.match(mail) != None:\n msg['mail'] = mail\n msg['topic'] = 'OpenScanCloud Registration (2/3)'\n msg['payload'] = 'Enter your first name'\n return msg\nmsg['payload'] = 'invalid input'\nreturn None,msg\n", "outputs": 2, "x": 390, "y": 1540, "wires": [ [ "2b02b97dd1614e52" ], [ "183a629accb417b1" ] ] }, { "id": "183a629accb417b1", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 530, "y": 1580, "wires": [ [] ] }, { "id": "2b02b97dd1614e52", "type": "ui_toast", "z": "e43a27722b508115", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 530, "y": 1540, "wires": [ [ "3e4c15d7b538f816" ] ] }, { "id": "3bf622f344172721", "type": "ui_toast", "z": "e43a27722b508115", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "SUBMIT", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 810, "y": 1540, "wires": [ [ "e431cb2b8d217cee" ] ] }, { "id": "e431cb2b8d217cee", "type": "python3-function", "z": "e43a27722b508115", "name": "msg", "func": "import requests\nimport os\nfrom OpenScan import OpenScanCloud\n\nif msg['payload'] == 'Cancel':\n return\n\nmsg['lastname'] = msg['payload']\n\nmsg2 = {}\n\nfor i in ['forename','lastname','mail']:\n msg2[i] = msg[i]\n\nr = OpenScanCloud('requestToken',msg2)\n\nstatus = r.status_code\n\nmsg['topic'] = 'OpenScanCloud Registration - Success'\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\n\nif status != 200:\n msg['topic'] = 'OpenScanCloud Registration - Failed'\n msg['payload'] = 'Registration failed, please try again.'\n\nmsg['status'] = status\n\nreturn msg", "outputs": 1, "x": 950, "y": 1540, "wires": [ [ "106874534890f229" ] ] }, { "id": "a38d7fde5c73210f", "type": "ui_button", "z": "e43a27722b508115", "name": "Register", "group": "12b719cba49817c9", "order": 6, "width": 2, "height": 1, "passthru": false, "label": "Register", "tooltip": "testtesttest", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "Please enter your email address:", "payloadType": "str", "topic": "Requesting an OpenScanCloud Token", "topicType": "str", "x": 100, "y": 1540, "wires": [ [ "95578e54a9b61cba" ] ] }, { "id": "106874534890f229", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 1090, "y": 1540, "wires": [ [] ] }, { "id": "5daca3ec47f8e7fc", "type": "python3-function", "z": "e43a27722b508115", "name": "msg", "func": "from OpenScan import load_int\n\nmsg = {}\n\ntry:\n msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\n msg['limit_filesize'] = float(int(load_int('osc_limit_filesize')/10000000))/100\n msg['limit_photos'] = load_int('osc_limit_photos')\n return msg\nexcept:\n pass", "outputs": 1, "x": 230, "y": 1400, "wires": [ [ "c9f0566601a3e130", "9bd86d27ea499a2a", "2c37f7030810d234" ] ] }, { "id": "f34de19d4cf810a9", "type": "comment", "z": "e43a27722b508115", "name": "Motor", "info": "", "x": 90, "y": 1740, "wires": [] }, { "id": "26c2b58e21f97475", "type": "comment", "z": "e43a27722b508115", "name": "Camera", "info": "", "x": 90, "y": 2500, "wires": [] }, { "id": "a8ec972bad47a9a8", "type": "comment", "z": "e43a27722b508115", "name": "Pinout", "info": "", "x": 90, "y": 2960, "wires": [] }, { "id": "b03e8b51187e88eb", "type": "ui_slider", "z": "e43a27722b508115", "name": "Rotor_delay (ms)", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 16, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "0.2", "step": "0.005", "className": "", "x": 450, "y": 2100, "wires": [ [ "11fd3363416433f9" ] ] }, { "id": "6aae9d4fddf08cc0", "type": "ui_slider", "z": "e43a27722b508115", "name": "tt delay", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 30, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "0.2", "step": "0.005", "className": "", "x": 420, "y": 2340, "wires": [ [ "e50492d1e18f43c6" ] ] }, { "id": "543e1690693acbeb", "type": "ui_slider", "z": "e43a27722b508115", "name": "rotor_acc", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 18, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.1", "max": "2", "step": "0.1", "className": "", "x": 420, "y": 2140, "wires": [ [ "e8b24efb0f30288e" ] ] }, { "id": "9a56c087d941f1da", "type": "ui_slider", "z": "e43a27722b508115", "name": "rotor_accramp", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 20, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "100", "max": "5000", "step": "100", "className": "", "x": 440, "y": 2180, "wires": [ [ "29f576be9e292232" ] ] }, { "id": "dfdebe10dbf0e198", "type": "ui_text_input", "z": "e43a27722b508115", "name": "rotor_stepsperrotation", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 14, "width": 3, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 460, "y": 2060, "wires": [ [ "78e256083f59f66f" ] ] }, { "id": "af8dfe78cbd0c301", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 19, "width": 3, "height": 1, "name": "rotor Accramp", "label": "Acceleration ramp", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 2140, "wires": [] }, { "id": "ee4b8908a5b83880", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 13, "width": 3, "height": 1, "name": "rotor_Steps per Rotation", "label": "Steps per Rotation", "format": "", "layout": "row-spread", "className": "", "x": 810, "y": 2180, "wires": [] }, { "id": "c4deaa38c1b0adbf", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 17, "width": 3, "height": 1, "name": "rotor Acc", "label": "Acceleration", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 2100, "wires": [] }, { "id": "baec873a95fff48a", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 15, "width": 3, "height": 1, "name": "rotor_delay", "label": "Delay", "format": "", "layout": "row-left", "className": "", "x": 770, "y": 2060, "wires": [] }, { "id": "355e89ab4e5484e4", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 26, "width": 6, "height": 1, "name": "tt", "label": "TURNTABLE", "format": "", "layout": "row-center", "className": "", "x": 90, "y": 2300, "wires": [] }, { "id": "10687d331a732790", "type": "ui_slider", "z": "e43a27722b508115", "name": "tt_acc", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 32, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.1", "max": "2", "step": "0.1", "className": "", "x": 410, "y": 2380, "wires": [ [ "af88b9da72917d62" ] ] }, { "id": "721b9680a3fa460e", "type": "ui_slider", "z": "e43a27722b508115", "name": "tt_accramp", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 34, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "500", "step": "1", "className": "", "x": 430, "y": 2420, "wires": [ [ "b1b4678827d3a6dd" ] ] }, { "id": "c6642c7470d3820c", "type": "ui_text_input", "z": "e43a27722b508115", "name": "tt_stepsperrotation", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 28, "width": 3, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 450, "y": 2300, "wires": [ [ "eef89545ec0f6aa8" ] ] }, { "id": "18e5918748660109", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 33, "width": 3, "height": 1, "name": "ttAccramp", "label": "Acceleration ramp", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 2420, "wires": [] }, { "id": "8e805244dc1899e8", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 27, "width": 3, "height": 1, "name": "tt_steps per Rotation", "label": "Steps per Rotation", "format": "", "layout": "row-spread", "className": "", "x": 800, "y": 2300, "wires": [] }, { "id": "a09e5fbea861bfb1", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 31, "width": 3, "height": 1, "name": "tt Acc", "label": "Acceleration", "format": "", "layout": "row-left", "className": "", "x": 750, "y": 2380, "wires": [] }, { "id": "7b06448b3b222011", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 29, "width": 3, "height": 1, "name": "tt_delay", "label": "Delay", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 2340, "wires": [] }, { "id": "0dfc86d90258f9bb", "type": "ui_slider", "z": "e43a27722b508115", "name": "rotor_angle", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 22, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "180", "step": "1", "className": "", "x": 430, "y": 2220, "wires": [ [ "c4b5a38c5c1df3d2" ] ] }, { "id": "9319d7d4f34c6d22", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 21, "width": 3, "height": 1, "name": "rotor_angle", "label": "Manual angle", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2220, "wires": [] }, { "id": "1610895f430b9aca", "type": "ui_slider", "z": "e43a27722b508115", "name": "tt_angle", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 36, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "180", "step": "1", "className": "", "x": 420, "y": 2460, "wires": [ [ "0f3367983bb8e159" ] ] }, { "id": "96a9febc0928b6f0", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 35, "width": 3, "height": 1, "name": "tt_angle", "label": "Manual angle", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2460, "wires": [] }, { "id": "e2c5ea8c16a5ea32", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 2, "width": 6, "height": 1, "name": "rotor", "label": "ROTOR", "format": "", "layout": "row-center", "className": "", "x": 90, "y": 1820, "wires": [] }, { "id": "277037c4716d85bf", "type": "ui_slider", "z": "e43a27722b508115", "name": "tt_dir", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 38, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "1", "className": "", "x": 410, "y": 2500, "wires": [ [ "c9d2e31514def4fc" ] ] }, { "id": "1361134e9847f003", "type": "ui_slider", "z": "e43a27722b508115", "name": "rotor_dir", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 24, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "1", "className": "", "x": 420, "y": 2260, "wires": [ [ "523717b0f218a5fd" ] ] }, { "id": "6b0d58943ecb8bb2", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 37, "width": 3, "height": 1, "name": "tt_dir", "label": "Direction", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2500, "wires": [] }, { "id": "08f93dd2aeedb391", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 23, "width": 3, "height": 1, "name": "rotor_dir", "label": "Direction", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2260, "wires": [] }, { "id": "46b91bef44714366", "type": "link in", "z": "e43a27722b508115", "name": "advanced settings", "links": [ "8750ad979e9ea246" ], "x": 95, "y": 100, "wires": [ [ "89eedf29b404f750" ] ] }, { "id": "8750ad979e9ea246", "type": "link out", "z": "e43a27722b508115", "name": "", "mode": "link", "links": [ "46b91bef44714366" ], "x": 955, "y": 460, "wires": [] }, { "id": "2522f888dc58972f", "type": "ui_slider", "z": "e43a27722b508115", "name": "cam_delay_before", "label": "", "tooltip": "", "group": "d324f0b852c2df0a", "order": 7, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "0.02", "className": "", "x": 430, "y": 2600, "wires": [ [ "5c752757090c49d2" ] ] }, { "id": "30e8df3d616512d8", "type": "ui_slider", "z": "e43a27722b508115", "name": "cam_gain", "label": "", "tooltip": "", "group": "d324f0b852c2df0a", "order": 11, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "10", "step": "0.1", "className": "", "x": 400, "y": 2640, "wires": [ [ "a1769f0277834f6d" ] ] }, { "id": "d855d926df89d65b", "type": "ui_slider", "z": "e43a27722b508115", "name": "cam_contrast", "label": "", "tooltip": "", "group": "d324f0b852c2df0a", "order": 13, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 420, "y": 2760, "wires": [ [ "1a8b0ba21b4f3005", "654bc70a18820828" ] ] }, { "id": "7617517dc8ba2859", "type": "ui_slider", "z": "e43a27722b508115", "name": "cam_saturation", "label": "", "tooltip": "", "group": "d324f0b852c2df0a", "order": 15, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 420, "y": 2800, "wires": [ [ "dc8fc962ff7d594b", "e64feb03a791ca33" ] ] }, { "id": "cbaa23c34e10fae1", "type": "ui_slider", "z": "e43a27722b508115", "name": "cam_jpeg_q", "label": "", "tooltip": "", "group": "d324f0b852c2df0a", "order": 3, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "100", "step": "1", "className": "", "x": 410, "y": 2840, "wires": [ [ "00e7836ccb3c4d0c" ] ] }, { "id": "bbe443b039a14e21", "type": "ui_text", "z": "e43a27722b508115", "group": "d324f0b852c2df0a", "order": 6, "width": 3, "height": 1, "name": "delay_before", "label": "Delay before", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2600, "wires": [] }, { "id": "d320ed3d701e6cc2", "type": "ui_text", "z": "e43a27722b508115", "group": "d324f0b852c2df0a", "order": 10, "width": 3, "height": 1, "name": "gain", "label": "Gain", "format": "", "layout": "row-spread", "className": "", "x": 740, "y": 2640, "wires": [] }, { "id": "f5834dd4646c8af9", "type": "ui_text", "z": "e43a27722b508115", "group": "d324f0b852c2df0a", "order": 12, "width": 3, "height": 1, "name": "contrast", "label": "Contrast", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2760, "wires": [] }, { "id": "ae9a4e19469813ef", "type": "ui_text", "z": "e43a27722b508115", "group": "d324f0b852c2df0a", "order": 14, "width": 3, "height": 1, "name": "saturation", "label": "Saturation", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2800, "wires": [] }, { "id": "bd629d0d31233c8b", "type": "ui_text", "z": "e43a27722b508115", "group": "d324f0b852c2df0a", "order": 2, "width": 3, "height": 1, "name": "jpegQ", "label": "Jpeg Quality", "format": "", "layout": "row-spread", "className": "", "x": 740, "y": 2840, "wires": [] }, { "id": "e89f61dbe6a6cffe", "type": "ui_text_input", "z": "e43a27722b508115", "name": "ext", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 3, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 390, "y": 3000, "wires": [ [ "885bc559fafec5f2" ] ] }, { "id": "ece38cb172a12d75", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 2, "width": 4, "height": 1, "name": "ext", "label": "External Camera", "format": "", "layout": "row-spread", "className": "", "x": 730, "y": 3000, "wires": [] }, { "id": "70014da0b6ab6698", "type": "ui_text_input", "z": "e43a27722b508115", "name": "light1", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 5, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 390, "y": 3040, "wires": [ [ "f70321c96bf81360" ] ] }, { "id": "29634ea5f6d666df", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 4, "width": 4, "height": 1, "name": "light1", "label": "Light 1", "format": "", "layout": "row-spread", "className": "", "x": 730, "y": 3040, "wires": [] }, { "id": "2544963852c6881a", "type": "ui_text_input", "z": "e43a27722b508115", "name": "light2", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 7, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 390, "y": 3080, "wires": [ [ "95e1603bbd06a69d" ] ] }, { "id": "27903533cd85a59e", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 6, "width": 4, "height": 1, "name": "light2", "label": "Light 2", "format": "", "layout": "row-spread", "className": "", "x": 730, "y": 3080, "wires": [] }, { "id": "a1394401246eb735", "type": "ui_text_input", "z": "e43a27722b508115", "name": "rotordir", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 9, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 400, "y": 3120, "wires": [ [ "a8f92ea6bf394640" ] ] }, { "id": "bc0aa4bacdfa94ea", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 8, "width": 4, "height": 1, "name": "rotordir", "label": "Rotor direction", "format": "", "layout": "row-spread", "className": "", "x": 740, "y": 3120, "wires": [] }, { "id": "f15ca4518b5f223e", "type": "ui_text_input", "z": "e43a27722b508115", "name": "rotorstep", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 11, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 400, "y": 3160, "wires": [ [ "06397bb46b3bb541" ] ] }, { "id": "0d2924b160e7e383", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 10, "width": 4, "height": 1, "name": "rotorstep", "label": "Rotor step", "format": "", "layout": "row-spread", "className": "", "x": 740, "y": 3160, "wires": [] }, { "id": "49900bb9047dd965", "type": "ui_text_input", "z": "e43a27722b508115", "name": "rotoren", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 13, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 400, "y": 3200, "wires": [ [ "687dcdc1ede11700" ] ] }, { "id": "a4d743ca73ee1622", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 12, "width": 4, "height": 1, "name": "rotoren", "label": "Rotor enable", "format": "", "layout": "row-spread", "className": "", "x": 740, "y": 3200, "wires": [] }, { "id": "5a90224dc998b417", "type": "ui_text_input", "z": "e43a27722b508115", "name": "ttdir", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 15, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 390, "y": 3240, "wires": [ [ "e220740c0d38ccb0" ] ] }, { "id": "67dc1b544c4ddf9f", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 14, "width": 4, "height": 1, "name": "ttdir", "label": "Turntable direction", "format": "", "layout": "row-spread", "className": "", "x": 730, "y": 3240, "wires": [] }, { "id": "d2364ab09627fe94", "type": "ui_text_input", "z": "e43a27722b508115", "name": "ttstep", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 17, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 390, "y": 3280, "wires": [ [ "79d7e5a705ab813a" ] ] }, { "id": "145b67ac40721ba6", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 16, "width": 4, "height": 1, "name": "ttstep", "label": "Turntable step", "format": "", "layout": "row-spread", "className": "", "x": 730, "y": 3280, "wires": [] }, { "id": "eef25405472acfee", "type": "ui_text_input", "z": "e43a27722b508115", "name": "endstop1", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 19, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 400, "y": 3320, "wires": [ [ "12d20f2274bcc511" ] ] }, { "id": "35eb252a41413531", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 18, "width": 4, "height": 1, "name": "endstop1", "label": "Endstop Rotor", "format": "", "layout": "row-spread", "className": "", "x": 740, "y": 3320, "wires": [] }, { "id": "74e455136b5ca5dd", "type": "ui_text_input", "z": "e43a27722b508115", "name": "endstop2", "label": "", "tooltip": "", "group": "70d0be671bf03ca7", "order": 21, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 400, "y": 3360, "wires": [ [ "a4a89668ce4c9f05" ] ] }, { "id": "3a74f653800eb831", "type": "ui_text", "z": "e43a27722b508115", "group": "70d0be671bf03ca7", "order": 20, "width": 4, "height": 1, "name": "endstop2", "label": "Endstop Turntable", "format": "", "layout": "row-spread", "className": "", "x": 740, "y": 3360, "wires": [] }, { "id": "5fcef1cb2e9e4788", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "confirm", "x": 680, "y": 460, "wires": [ [ "29745a36fc157f3f" ] ] }, { "id": "f06a7bcad524e9f9", "type": "python3-function", "z": "e43a27722b508115", "name": "msg", "func": "from OpenScan import save, load_bool\n\nif msg['payload'] == True and not load_bool('advanced_settings'):\n msg['payload'] = '''

PLEASE READ :)

\n

Modifying the advanced settings can potentially damage your device and/or the connected peripherals.

\n

Please read the given information texts carefully and only change settings, when you are sure about the consequences!

\n'''\n return msg\nelif not msg['payload']: \n save('advanced_settings', False)\n", "outputs": 1, "x": 530, "y": 460, "wires": [ [ "5fcef1cb2e9e4788" ] ] }, { "id": "f455fb39039617ae", "type": "ui_slider", "z": "e43a27722b508115", "name": "cam_rotation", "label": "", "tooltip": "", "group": "d324f0b852c2df0a", "order": 5, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "270", "step": "90", "className": "", "x": 410, "y": 2880, "wires": [ [ "3019576de193d9d6" ] ] }, { "id": "fdfbc900fe424eb9", "type": "ui_text", "z": "e43a27722b508115", "group": "d324f0b852c2df0a", "order": 4, "width": 3, "height": 1, "name": "cam_rot", "label": "Image Rotation", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2880, "wires": [] }, { "id": "c3699d6b9664ccca", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2060, "wires": [ [ "dfdebe10dbf0e198" ] ] }, { "id": "78e256083f59f66f", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2060, "wires": [ [] ] }, { "id": "0f9141b401322374", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2180, "wires": [ [ "9a56c087d941f1da" ] ] }, { "id": "29f576be9e292232", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2180, "wires": [ [] ] }, { "id": "23e3099b34c4e475", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2220, "wires": [ [ "0dfc86d90258f9bb" ] ] }, { "id": "c4b5a38c5c1df3d2", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2220, "wires": [ [] ] }, { "id": "79a14162ac805fac", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2260, "wires": [ [ "1361134e9847f003" ] ] }, { "id": "523717b0f218a5fd", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nif (msg.payload === 1){\n content = '1'\n}\nelse{\n content = '-1'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2260, "wires": [ [] ] }, { "id": "f5cf780f3fa8997e", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'rotor_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) * 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2100, "wires": [ [ "b03e8b51187e88eb" ] ] }, { "id": "11fd3363416433f9", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload / 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2100, "wires": [ [] ] }, { "id": "02060b3f3b294563", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'rotor_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2140, "wires": [ [ "543e1690693acbeb" ] ] }, { "id": "e8b24efb0f30288e", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2140, "wires": [ [] ] }, { "id": "de1ad8b27b72a5ac", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'tt_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nsteps = parseInt(data);\nif (steps == 3600){\n steps = 3200\n}\n\nmsg.payload = steps\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2300, "wires": [ [ "c6642c7470d3820c" ] ] }, { "id": "ed4d587cb4feb064", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'tt_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2420, "wires": [ [ "721b9680a3fa460e" ] ] }, { "id": "5b02160c33605ae7", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'tt_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2460, "wires": [ [ "1610895f430b9aca" ] ] }, { "id": "304c135ec09801e3", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2500, "wires": [ [ "277037c4716d85bf" ] ] }, { "id": "a91dcbe0f9a2416a", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'tt_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) * 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2340, "wires": [ [ "6aae9d4fddf08cc0" ] ] }, { "id": "6b2eb1cb95e573f9", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'tt_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2380, "wires": [ [ "10687d331a732790" ] ] }, { "id": "eef89545ec0f6aa8", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'tt_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2300, "wires": [ [] ] }, { "id": "b1b4678827d3a6dd", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'tt_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2420, "wires": [ [] ] }, { "id": "0f3367983bb8e159", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'tt_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2460, "wires": [ [] ] }, { "id": "c9d2e31514def4fc", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nif (msg.payload === 1){\n content = '1'\n}\nelse{\n content = '-1'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2500, "wires": [ [] ] }, { "id": "e50492d1e18f43c6", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'tt_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload / 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2340, "wires": [ [] ] }, { "id": "af88b9da72917d62", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'tt_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2380, "wires": [ [] ] }, { "id": "43fe948b3e7234e2", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'cam_delay_before'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 2600, "wires": [ [ "2522f888dc58972f" ] ] }, { "id": "5c752757090c49d2", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'cam_delay_before'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 2600, "wires": [ [] ] }, { "id": "435681b3f7625a7e", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'cam_gain'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 2640, "wires": [ [ "30e8df3d616512d8" ] ] }, { "id": "a1769f0277834f6d", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'cam_gain'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 2640, "wires": [ [] ] }, { "id": "1de07c7d285cbaf3", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'cam_contrast'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 2760, "wires": [ [ "d855d926df89d65b" ] ] }, { "id": "1a8b0ba21b4f3005", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'cam_contrast'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 2760, "wires": [ [] ] }, { "id": "ebc9e283468eda31", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'cam_saturation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 2800, "wires": [ [ "7617517dc8ba2859" ] ] }, { "id": "dc8fc962ff7d594b", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'cam_saturation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 2800, "wires": [ [] ] }, { "id": "60d641613527c736", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'cam_jpeg_quality'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 2840, "wires": [ [ "cbaa23c34e10fae1" ] ] }, { "id": "00e7836ccb3c4d0c", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'cam_jpeg_quality'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 2840, "wires": [ [] ] }, { "id": "7f24c0c34a88ba04", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'cam_rotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 2880, "wires": [ [ "f455fb39039617ae" ] ] }, { "id": "3019576de193d9d6", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'cam_rotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 2880, "wires": [ [] ] }, { "id": "77bb7dc529d63a7e", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_external'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3000, "wires": [ [ "e89f61dbe6a6cffe" ] ] }, { "id": "885bc559fafec5f2", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_external'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3000, "wires": [ [] ] }, { "id": "cc6dabe017a9c8a8", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_rotor_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3320, "wires": [ [ "eef25405472acfee" ] ] }, { "id": "12d20f2274bcc511", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_rotor_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3320, "wires": [ [] ] }, { "id": "dcb9fed8122759fd", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_ringlight1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3040, "wires": [ [ "70014da0b6ab6698" ] ] }, { "id": "f70321c96bf81360", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_ringlight1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3040, "wires": [ [] ] }, { "id": "013d2057c2347a62", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_ringlight2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3080, "wires": [ [ "2544963852c6881a" ] ] }, { "id": "95e1603bbd06a69d", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_ringlight2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3080, "wires": [ [] ] }, { "id": "f88bbf11d5aa9a14", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3120, "wires": [ [ "a1394401246eb735" ] ] }, { "id": "a8f92ea6bf394640", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3120, "wires": [ [] ] }, { "id": "301af70731e096e5", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_rotor_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3160, "wires": [ [ "f15ca4518b5f223e" ] ] }, { "id": "06397bb46b3bb541", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_rotor_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3160, "wires": [ [] ] }, { "id": "0456a9ec4c236c9e", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_rotor_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3200, "wires": [ [ "49900bb9047dd965" ] ] }, { "id": "687dcdc1ede11700", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_rotor_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3200, "wires": [ [] ] }, { "id": "09d37ba08ec0f163", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3240, "wires": [ [ "5a90224dc998b417" ] ] }, { "id": "37d954a4cf7e87ea", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_tt_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3280, "wires": [ [ "d2364ab09627fe94" ] ] }, { "id": "e220740c0d38ccb0", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3240, "wires": [ [] ] }, { "id": "79d7e5a705ab813a", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_tt_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3280, "wires": [ [] ] }, { "id": "21dc963d967d9c99", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'pin_tt_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 3360, "wires": [ [ "74e455136b5ca5dd" ] ] }, { "id": "a4a89668ce4c9f05", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'pin_tt_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 590, "y": 3360, "wires": [ [] ] }, { "id": "22ef66b0e2058be2", "type": "function", "z": "e43a27722b508115", "name": "loadB", "func": "var file = 'ssh'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 360, "wires": [ [ "cb3437ec113e1b6f" ] ] }, { "id": "9ce01c8ba97932c1", "type": "function", "z": "e43a27722b508115", "name": "loadB", "func": "var file = 'smb'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 400, "wires": [ [ "60fd0adce1cfeb82" ] ] }, { "id": "81356177176eebcf", "type": "function", "z": "e43a27722b508115", "name": "loadB", "func": "var file = 'advanced_settings'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 460, "wires": [ [ "f6d6cc35679ede63" ] ] }, { "id": "b78346ca3ce70c68", "type": "function", "z": "e43a27722b508115", "name": "msg", "func": "msg.payload = 'This is a free piece of software and it is provided as is, without any warranty.
There might be functions that need a connection to the internet: '+\n '

By pressing GET FEATURES you agree that the shown preview image will be transfered, stored and processed via SFTP to my servers '+\n '(Thomas Megel, OpenScan, Halle, Germany). The IP address will be saved for 14 days The images might be used for further experiments (e.g. machine learning, automation ...). '+\n '

By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\n 'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\n 'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\n '

By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\n 'The raw images and resulting 3d models will never be published without your explicit consent.'+ \n '

If you have any questions you can contact me at info@openscan.eu.'+ \n '

THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\n 'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \n 'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \n 'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 320, "wires": [ [ "f0d8dbcca76a1926" ] ] }, { "id": "e95b86cbac1b03b9", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var data\n\nif(msg.payload === 'Agree'){\n data = true;\n}\nelse{\n data = false;\n}\nvar file = 'terms'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nfs.writeFile(filepath+file, String(data), err => {\n if (err) {\n return msg\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 550, "y": 320, "wires": [ [] ] }, { "id": "3e4c15d7b538f816", "type": "function", "z": "e43a27722b508115", "name": "msg", "func": "if (msg.payload === 'Cancel'){\n return\n}\nmsg.forename = msg.payload\nmsg.topic = 'OpenScanCloud Registration (3/3)'\nmsg.payload = 'Enter your last name'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 670, "y": 1540, "wires": [ [ "3bf622f344172721" ] ] }, { "id": "0f0871baf322b6d0", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_anglemin'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1820, "wires": [ [ "6ebd15c61a5ca891" ] ] }, { "id": "f21a95a732fadae6", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 5, "width": 3, "height": 1, "name": "rotor_anglemin", "label": "Min Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1820, "wires": [] }, { "id": "acd10a4c99ee8063", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_anglemin'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1820, "wires": [ [] ] }, { "id": "6ebd15c61a5ca891", "type": "ui_slider", "z": "e43a27722b508115", "name": "rotor_anglemin", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 6, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1820, "wires": [ [ "acd10a4c99ee8063" ] ] }, { "id": "3ad0f0f206e4a873", "type": "ui_slider", "z": "e43a27722b508115", "name": "rotor_anglemax", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 8, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1860, "wires": [ [ "031d7697768d0e77" ] ] }, { "id": "3b6d759ed5be647f", "type": "ui_slider", "z": "e43a27722b508115", "name": "rotor_anglestart", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 4, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1900, "wires": [ [ "be1954dd71d2c94c" ] ] }, { "id": "edb1c8fae8b65c82", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_anglemax'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1860, "wires": [ [ "3ad0f0f206e4a873" ] ] }, { "id": "031d7697768d0e77", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_anglemax'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1860, "wires": [ [] ] }, { "id": "462a8f3ca75fc3c8", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_anglestart'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1900, "wires": [ [ "3b6d759ed5be647f" ] ] }, { "id": "be1954dd71d2c94c", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_anglestart'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1900, "wires": [ [] ] }, { "id": "3d7379753d2eda25", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 7, "width": 3, "height": 1, "name": "rotor_anglemax", "label": "Max Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1860, "wires": [] }, { "id": "9cc86d1bcae3ab4e", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 3, "width": 3, "height": 1, "name": "rotor_anglestart", "label": "Start Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1900, "wires": [] }, { "id": "2e9b29c70969cf01", "type": "link in", "z": "e43a27722b508115", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 135, "y": 360, "wires": [ [ "22ef66b0e2058be2", "9ce01c8ba97932c1", "81356177176eebcf" ] ] }, { "id": "592ec13d8f8923a9", "type": "link in", "z": "e43a27722b508115", "name": "ip address", "links": [ "50eeb3e362f9027f", "960912e90ba5b5bc", "eb1a2387a1eeea76", "c994c779e4bad800" ], "x": 85, "y": 940, "wires": [ [ "ded3086945a6d4b5", "6ea3cdab41f20f92" ] ] }, { "id": "cb40b9341bd22a28", "type": "link in", "z": "e43a27722b508115", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 185, "y": 1820, "wires": [ [ "0f0871baf322b6d0", "edb1c8fae8b65c82", "462a8f3ca75fc3c8", "c3699d6b9664ccca", "f5cf780f3fa8997e", "02060b3f3b294563", "0f9141b401322374", "23e3099b34c4e475", "79a14162ac805fac", "de1ad8b27b72a5ac", "a91dcbe0f9a2416a", "6b2eb1cb95e573f9", "ed4d587cb4feb064", "5b02160c33605ae7", "304c135ec09801e3", "f036424d79645761", "b7db72b7f0599ebd" ] ] }, { "id": "d1efcd5fa9d25785", "type": "link in", "z": "e43a27722b508115", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 155, "y": 2540, "wires": [ [ "43fe948b3e7234e2", "435681b3f7625a7e", "1de07c7d285cbaf3", "ebc9e283468eda31", "60d641613527c736", "7f24c0c34a88ba04", "6281b2e6e081104d" ] ] }, { "id": "da61581182b7299e", "type": "link in", "z": "e43a27722b508115", "name": "enable projectname", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 135, "y": 3000, "wires": [ [ "77bb7dc529d63a7e", "dcb9fed8122759fd", "013d2057c2347a62", "f88bbf11d5aa9a14", "301af70731e096e5", "0456a9ec4c236c9e", "09d37ba08ec0f163", "37d954a4cf7e87ea", "cc6dabe017a9c8a8", "21dc963d967d9c99" ] ] }, { "id": "7e1c84ec516ad0a6", "type": "ui_button", "z": "e43a27722b508115", "name": "Reset default", "group": "4390b2ebcbbe104c", "order": 6, "width": 6, "height": 1, "passthru": false, "label": "Restore default settings", "tooltip": "", "color": "red", "bgcolor": "", "className": "", "icon": "", "payload": "This can not be undone!", "payloadType": "str", "topic": "Restore default settings?", "topicType": "str", "x": 110, "y": 620, "wires": [ [ "53e6681d7254d484" ] ] }, { "id": "53e6681d7254d484", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 270, "y": 620, "wires": [ [ "c11e79cfa7bc10b7" ] ] }, { "id": "c11e79cfa7bc10b7", "type": "function", "z": "e43a27722b508115", "name": "msg", "func": "msg.overwrite = true\nif(msg.payload == \"Yes\"){\n return msg}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 620, "wires": [ [ "307782d10c1acdaf" ] ] }, { "id": "307782d10c1acdaf", "type": "link out", "z": "e43a27722b508115", "name": "", "mode": "link", "links": [ "38783aea9cc317a6" ], "x": 505, "y": 620, "wires": [] }, { "id": "5fff689f9f8bc1ca", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": true, "className": "", "topic": "", "name": "Info", "x": 1010, "y": 140, "wires": [ [] ] }, { "id": "cca3300a8f0daf4d", "type": "ui_button", "z": "e43a27722b508115", "name": "Update&Info", "group": "ddbd496e.93a288", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Update&Log

Status

See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on Github.com for details and the source code.

Updatetype

- stable: latest well-tested and mostly bug-free version for the OpenScanMini or Classic and various cameras

- beta: stable version + some experimental and new features, which might bring joy and some new bugs as well

- mini: very simplified firmware for the OpenScanMini + Arducam IMX519

Auto-Check update availability

Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on Github.com

This option is activated by default.

Check Updates

Alternatively, you can check for updates manually at any time by pressing this button.

Download Error Log

In case you encounter any errors with your device, please download the error log text and send a copy to info@openscan.eu or create an issue on Github.com

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 750, "y": 180, "wires": [ [ "5fff689f9f8bc1ca" ] ] }, { "id": "654bc70a18820828", "type": "python3-function", "z": "e43a27722b508115", "name": "", "func": "from OpenScan import camera\n\ncamera(\"/picam2_contrast?contrast=\" + str(msg['payload']))", "outputs": 1, "x": 660, "y": 2720, "wires": [ [] ] }, { "id": "e64feb03a791ca33", "type": "python3-function", "z": "e43a27722b508115", "name": "", "func": "from OpenScan import camera\n\ncamera(\"/picam2_saturation?saturation=\" + str(msg['payload']))", "outputs": 1, "x": 660, "y": 2680, "wires": [ [] ] }, { "id": "81bd4381cd029958", "type": "ui_slider", "z": "e43a27722b508115", "name": "cam_delay_after", "label": "", "tooltip": "", "group": "d324f0b852c2df0a", "order": 9, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "0.02", "className": "", "x": 440, "y": 2560, "wires": [ [ "e612073aded01a8f" ] ] }, { "id": "0d92559980944ae3", "type": "ui_text", "z": "e43a27722b508115", "group": "d324f0b852c2df0a", "order": 8, "width": 3, "height": 1, "name": "delay_after", "label": "Delay after", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2560, "wires": [] }, { "id": "6281b2e6e081104d", "type": "function", "z": "e43a27722b508115", "name": "loadF", "func": "var file = 'cam_delay_after'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 2560, "wires": [ [ "81bd4381cd029958" ] ] }, { "id": "e612073aded01a8f", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'cam_delay_after'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 620, "y": 2560, "wires": [ [] ] }, { "id": "e2411b49791840e0", "type": "python3-function", "z": "e43a27722b508115", "name": "reboot", "func": "import os\nfrom time import sleep\nsleep(1.5)\nimport RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nos.system('reboot -h')\n", "outputs": 1, "x": 270, "y": 520, "wires": [ [] ] }, { "id": "01c882fcc51b349c", "type": "link in", "z": "e43a27722b508115", "name": "reboot", "links": [ "16c76929f88df841", "fe3a855fee9e28c6", "09d4a9c756161e10" ], "x": 155, "y": 520, "wires": [ [ "e2411b49791840e0" ] ] }, { "id": "e51dd5e5c0f050d6", "type": "ui_text_input", "z": "e43a27722b508115", "name": "", "label": "SSID", "tooltip": "", "group": "8ab79a98e536e0d6", "order": 4, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "ssid", "sendOnBlur": true, "className": "", "topicType": "str", "x": 210, "y": 980, "wires": [ [ "a7d233f984009e2e" ] ] }, { "id": "9959649037cb063b", "type": "ui_text_input", "z": "e43a27722b508115", "name": "", "label": "Password", "tooltip": "", "group": "8ab79a98e536e0d6", "order": 5, "width": 6, "height": 1, "passthru": false, "mode": "password", "delay": "0", "topic": "password", "sendOnBlur": true, "className": "", "topicType": "str", "x": 220, "y": 1020, "wires": [ [ "a7d233f984009e2e" ] ] }, { "id": "1d42cb9a63409283", "type": "ui_text_input", "z": "e43a27722b508115", "name": "", "label": "Country Code 2", "tooltip": "", "group": "8ab79a98e536e0d6", "order": 6, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "country", "sendOnBlur": true, "className": "", "topicType": "str", "x": 240, "y": 1060, "wires": [ [ "a7d233f984009e2e" ] ] }, { "id": "84ecaafd629c0f7a", "type": "ui_button", "z": "e43a27722b508115", "name": "", "group": "8ab79a98e536e0d6", "order": 7, "width": 0, "height": 0, "passthru": false, "label": "Connect to Wifi", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "connect", "topicType": "str", "x": 240, "y": 1100, "wires": [ [ "a7d233f984009e2e" ] ] }, { "id": "6ea3cdab41f20f92", "type": "ui_text", "z": "e43a27722b508115", "group": "8ab79a98e536e0d6", "order": 2, "width": 0, "height": 0, "name": "", "label": "Hotspot Mode", "format": "{{msg.mode}}", "layout": "row-spread", "className": "", "x": 240, "y": 900, "wires": [] }, { "id": "a7d233f984009e2e", "type": "function", "z": "e43a27722b508115", "name": "function 1", "func": "if (msg.topic == \"ssid\"){\n global.set('network_ssid',msg.payload)\n}\nelse if (msg.topic == \"password\"){\n global.set('network_password',msg.payload)\n}\nelse if (msg.topic == \"country\"){\n global.set('network_country',msg.payload)\n}\nelse if (msg.topic == \"connect\"){\n msg.ssid = global.get('network_ssid')\n msg.password = global.get('network_password')\n msg.country = global.get('network_country')\n msg.payload = \"\"\n return msg\n}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 440, "y": 980, "wires": [ [ "9b851aa999e86fd7", "021dc780b478fee6", "9ec0ad9fd3687e9f" ] ] }, { "id": "65518f3d4e3095e5", "type": "link in", "z": "e43a27722b508115", "name": "link in 1", "links": [ "200d4b9951b6e066" ], "x": 85, "y": 980, "wires": [ [ "e51dd5e5c0f050d6", "9959649037cb063b", "1d42cb9a63409283" ] ] }, { "id": "9b851aa999e86fd7", "type": "python3-function", "z": "e43a27722b508115", "name": "", "func": "from OpenScan import add_wifi_network, check_hotspot_mode\nfrom time import sleep\n\nsleep(0.5)\n\nerror = \"\"\nif msg['ssid'] == \"\":\n error = \"SSID, \"\nif msg['password'] == \"\" or len(msg['password'])<8:\n error = error + \"password, \"\nif msg['country'] == \"\" or len(msg['country']) != 2:\n error = error + \"country code\"\n\nif error != \"\":\n msg['payload'] = error\n msg['topic'] = \"Invalid Input(s):\"\n if check_hotspot_mode():\n msg['mode'] = True\n else:\n msg['mode'] = False\n return msg\n\n\nmsg['result'] = add_wifi_network(msg['ssid'],msg['password'],msg['country'])\n\nsleep(3)\n\nif check_hotspot_mode():\n msg['mode'] = True\nelse:\n msg['mode'] = False\n\nmsg['topic'] = \"Added wifi & connected\"\nmsg['payload'] = \"changes might take a moment ;)\"\n\nreturn msg", "outputs": 1, "x": 670, "y": 980, "wires": [ [ "c994c779e4bad800", "11b19e9c6a4ffd8d", "36890eb99a2ca1cf" ] ] }, { "id": "11b19e9c6a4ffd8d", "type": "ui_toast", "z": "e43a27722b508115", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 870, "y": 980, "wires": [ [] ] }, { "id": "021dc780b478fee6", "type": "debug", "z": "e43a27722b508115", "name": "debug 3", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 640, "y": 920, "wires": [] }, { "id": "c994c779e4bad800", "type": "link out", "z": "e43a27722b508115", "name": "link out 2", "mode": "link", "links": [ "592ec13d8f8923a9" ], "x": 815, "y": 1020, "wires": [] }, { "id": "1eef47e0074545a9", "type": "python3-function", "z": "e43a27722b508115", "name": "", "func": "from OpenScan import add_wifi_network, check_hotspot_mode\n\nif check_hotspot_mode():\n msg['mode'] = True\nelse:\n msg['mode'] = False\n\nreturn msg", "outputs": 2, "x": 670, "y": 1100, "wires": [ [ "c994c779e4bad800", "36890eb99a2ca1cf" ], [] ] }, { "id": "434b04d8a65951ce", "type": "inject", "z": "e43a27722b508115", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 440, "y": 1140, "wires": [ [ "1eef47e0074545a9" ] ] }, { "id": "9ec0ad9fd3687e9f", "type": "ui_toast", "z": "e43a27722b508115", "position": "bottom right", "displayTime": "5", "highlight": "", "sendall": true, "outputs": 0, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "Adding new Wifi", "name": "", "x": 670, "y": 1020, "wires": [] }, { "id": "36890eb99a2ca1cf", "type": "debug", "z": "e43a27722b508115", "name": "debug 4", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 860, "y": 940, "wires": [] }, { "id": "6b7245c3dcb694c8", "type": "ui_slider", "z": "e43a27722b508115", "name": "endstop_angle", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 12, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "1", "className": "", "x": 440, "y": 2020, "wires": [ [ "85ad07b8f973bbe2" ] ] }, { "id": "69516440e3997111", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 11, "width": 3, "height": 1, "name": "endstop_angle", "label": "Endstop angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 2020, "wires": [] }, { "id": "85ad07b8f973bbe2", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_endstop_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2020, "wires": [ [] ] }, { "id": "f036424d79645761", "type": "function", "z": "e43a27722b508115", "name": "loadI", "func": "var file = 'rotor_endstop_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2020, "wires": [ [ "6b7245c3dcb694c8" ] ] }, { "id": "253feafa5a2f8b1d", "type": "ui_switch", "z": "e43a27722b508115", "name": "rotor_enable_endstop", "label": "", "tooltip": "", "group": "7a3279eea439bcdd", "order": 10, "width": 3, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 460, "y": 1940, "wires": [ [ "1916dc3fd04f0664", "6cb92b9b9f0d6954" ] ] }, { "id": "b7db72b7f0599ebd", "type": "function", "z": "e43a27722b508115", "name": "loadB", "func": "var file = 'rotor_enable_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nvar data = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1940, "wires": [ [ "253feafa5a2f8b1d" ] ] }, { "id": "1916dc3fd04f0664", "type": "function", "z": "e43a27722b508115", "name": "write", "func": "var file = 'rotor_enable_endstop'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1940, "wires": [ [] ] }, { "id": "de409e57a0c4bf41", "type": "ui_text", "z": "e43a27722b508115", "group": "7a3279eea439bcdd", "order": 9, "width": 3, "height": 1, "name": "rotor_enable_endstop", "label": "Enable Endstop", "format": "", "layout": "row-left", "className": "", "x": 800, "y": 1940, "wires": [] }, { "id": "6cb92b9b9f0d6954", "type": "function", "z": "e43a27722b508115", "name": "msg", "func": "msg.enabled = msg.payload\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 1980, "wires": [ [ "69516440e3997111", "f036424d79645761" ] ] }, { "id": "4c7fa5b5b27b83a5", "type": "python3-function", "z": "a5557543ccff5889", "name": "create beta new", "func": "import json\nimport requests\nimport shutil\n\n#scope = 'main'\nscope = 'betaArdu'\n\nupdatepath = '/home/pi/OpenScan/tmp/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\n\n## load update.json\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'download update.json failed'\n return msg\n\nmsg = {}\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg2 = msg.copy()\nif scope in msg:\n del msg[scope]\n\nmsg[scope]={}\nmsg[scope]['1'] = {}\nmsg[scope]['1']['src'] = scope + '/fla.py'\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\n\nmsg[scope]['2'] = {}\nmsg[scope]['2']['src'] = scope + '/OpenScan.py'\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\n\nmsg[scope]['3'] = {}\nmsg[scope]['3']['src'] = scope + '/config.txt'\nmsg[scope]['3']['dst'] = '/boot/config.txt'\n\nmsg[scope]['4'] = {}\nmsg[scope]['4']['src'] = scope + '/flows.json'\nmsg[scope]['4']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\n\nmsg[scope]['5'] = {}\nmsg[scope]['5']['src'] = scope + '/settings.js'\nmsg[scope]['5']['dst'] = '/root/.node-red/settings.js'\n\n#msg[scope]['6'] = {}\n#msg[scope]['6']['src'] = 'files/logo.jpg'\n#msg[scope]['6']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\n\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n filesize = os.path.getsize(filepath)\n msg[scope][i]['filesize'] = filesize\n\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\n os.system('rm -r /home/pi/OpenScan/tmp/update') \nos.makedirs('/home/pi/OpenScan/tmp/update/')\n\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\n json.dump(msg, f, indent=4)\n\nfor i in msg[scope]:\n if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\n os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\n shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\n\nmsg['payload'] = 'created with scope: ' + scope\n\nreturn msg", "outputs": 1, "x": 260, "y": 140, "wires": [ [ "e23c514008cad1a1" ] ] }, { "id": "80175eb8dc6ad009", "type": "inject", "z": "a5557543ccff5889", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 100, "y": 140, "wires": [ [ "4c7fa5b5b27b83a5" ] ] }, { "id": "d7362e6e0ec7bdaa", "type": "inject", "z": "a5557543ccff5889", "name": "", "props": [ { "p": "overwrite", "v": "true", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 90, "y": 220, "wires": [ [ "4ce127c61c3c5966", "beacc3dc5398fa79" ] ] }, { "id": "4ce127c61c3c5966", "type": "python3-function", "z": "a5557543ccff5889", "name": "prepare image creation", "func": "import os\n\n#factory reset, reset wpa, create wpa in boot, rm files\n#should be done before creating a new raspbian image\n\nbasepath = '/home/pi/OpenScan/'\n\n#remove files\n\ndir = basepath + 'scans/'\n\nfor i in ['scans/','tmp/']:\n os.system('rm -r ' + basepath + i)\n os.mkdir(basepath + i)\n\n#delete wifi\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\nwith open(temp_dir, 'w+') as file:\n file.write('update_config=1\\nctrl_interface=DIR=/var/run/wpa_supplicant\\ncountry=de\\n\\n')\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\nos.system('wpa_cli -i wlan0 reconfigure')\n\n#create new wpa_supplicant.conf\nwith open('/boot/wpa_supplicant.conf','w+') as file:\n file.write('country=de\\nupdate_config=1\\nctrl_interface=/var/run/wpa_supplicant\\n\\nnetwork={\\n scan_ssid=1\\n ssid=\"wlan name\"\\n psk=\"xxxx\"\\n}')\nos.system(\"chmod a+rwx /boot/wpa_supplicant.conf\")\n\n\n#rm tmp dir\n\n\n#stop photos:\nos.system('systemctl stop flask')\nos.system('rm -r ' + basepath + 'tmp')\nos.system('mkdir ' + basepath + 'tmp')\n\nos.system('systemctl stop nodered')\n\n#reset factory\n\n", "outputs": 1, "x": 290, "y": 220, "wires": [ [] ] }, { "id": "beacc3dc5398fa79", "type": "link out", "z": "a5557543ccff5889", "name": "", "mode": "link", "links": [ "38783aea9cc317a6" ], "x": 195, "y": 260, "wires": [] }, { "id": "e23c514008cad1a1", "type": "debug", "z": "a5557543ccff5889", "name": "debug 1", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 480, "y": 140, "wires": [] }, { "id": "b0629875a30ae1d7", "type": "python3-function", "z": "a5557543ccff5889", "name": "get update", "func": "import json\nimport requests\nfrom OpenScan import load_str\n\nif not msg['payload']:\n msg['status'] = '--READY--'\n return msg\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'no internet connection'\n return msg\n\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg['status'] = 'checking updates'\n\nreturn msg, msg", "outputs": 2, "x": 390, "y": 540, "wires": [ [ "1bbe2d769f42c313" ], [ "fefe45404bdb19c4" ] ] }, { "id": "c7b6d05a62172432", "type": "ui_text", "z": "a5557543ccff5889", "group": "ddbd496e.93a288", "order": 3, "width": 0, "height": 0, "name": "", "label": "Status:", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 210, "y": 400, "wires": [] }, { "id": "fefe45404bdb19c4", "type": "python3-function", "z": "a5557543ccff5889", "name": "check files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str\n\nscope = load_str('update_type')\nmsg['scope'] = scope\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_old = updatepath + 'update.json'\n\ncounter = 0\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n if os.path.isfile(temp):\n filesize = os.path.getsize(temp)\n msg[scope][i]['filesize2'] = filesize\n if filesize == msg[scope][i]['filesize']:\n msg[scope][i]['update'] = False\n continue\n msg[scope][i]['update'] = True\n\n counter += 1\n\nif counter == 0:\n msg['status'] = 'No new update available'\nelse:\n msg['status'] = 'New update available'\n msg['topic'] = msg['status']\n msg['payload'] = 'Install & reboot now?'\n\nmsg['counter'] = counter\n\nreturn msg\n", "outputs": 1, "x": 550, "y": 560, "wires": [ [ "1bbe2d769f42c313", "ae92a328af306ebb" ] ] }, { "id": "d0104e0163745993", "type": "link in", "z": "a5557543ccff5889", "name": "", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 115, "y": 440, "wires": [ [ "ec30638407332e43", "38cbf7965d1c1834", "49f1ecb29a3f84f4" ] ] }, { "id": "ec30638407332e43", "type": "function", "z": "a5557543ccff5889", "name": "loadS", "func": "var file = 'update_type'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data.replace(/(\\r\\n|\\n|\\r)/gm,\"\"));\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 210, "y": 480, "wires": [ [ "2852023f3aa8db10" ] ] }, { "id": "2852023f3aa8db10", "type": "ui_dropdown", "z": "a5557543ccff5889", "name": "", "label": "", "tooltip": "", "place": "Select option", "group": "ddbd496e.93a288", "order": 5, "width": 2, "height": 1, "passthru": false, "multiple": false, "options": [ { "label": "stable", "value": "main", "type": "str" }, { "label": "beta", "value": "beta", "type": "str" }, { "label": "betaArdu", "value": "betaArdu", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 340, "y": 480, "wires": [ [ "1e10b387ee30c486" ] ] }, { "id": "1e10b387ee30c486", "type": "function", "z": "a5557543ccff5889", "name": "write", "func": "var file = 'update_type'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 480, "wires": [ [] ] }, { "id": "274129c51b0b87ef", "type": "ui_text", "z": "a5557543ccff5889", "group": "ddbd496e.93a288", "order": 4, "width": 4, "height": 1, "name": "", "label": "Updatetype: ", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 610, "y": 480, "wires": [] }, { "id": "51cd8c8643e6b46a", "type": "ui_switch", "z": "a5557543ccff5889", "name": "", "label": "Auto-check update availability", "tooltip": "", "group": "ddbd496e.93a288", "order": 6, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 410, "y": 440, "wires": [ [ "1ab4c6b4b232a022" ] ] }, { "id": "38cbf7965d1c1834", "type": "function", "z": "a5557543ccff5889", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 210, "y": 440, "wires": [ [ "51cd8c8643e6b46a" ] ] }, { "id": "1ab4c6b4b232a022", "type": "function", "z": "a5557543ccff5889", "name": "write", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 440, "wires": [ [] ] }, { "id": "ae92a328af306ebb", "type": "ui_toast", "z": "a5557543ccff5889", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "NO", "cancel": "YES", "raw": false, "className": "", "topic": "", "name": "", "x": 710, "y": 560, "wires": [ [ "2de63e8e3ae5fb0c", "929281fef53e09f8" ] ] }, { "id": "cbd0afc4aa7b302a", "type": "link in", "z": "a5557543ccff5889", "name": "update status", "links": [ "1bbe2d769f42c313", "42061b28cff81f99" ], "x": 115, "y": 400, "wires": [ [ "c7b6d05a62172432", "c94623ddd9d95f78" ] ] }, { "id": "1bbe2d769f42c313", "type": "link out", "z": "a5557543ccff5889", "name": "", "mode": "link", "links": [ "cbd0afc4aa7b302a" ], "x": 665, "y": 520, "wires": [] }, { "id": "7cf60615d93e696b", "type": "ui_button", "z": "a5557543ccff5889", "name": "", "group": "ddbd496e.93a288", "order": 7, "width": 6, "height": 1, "passthru": false, "label": "Check Updates", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 180, "y": 560, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "2de63e8e3ae5fb0c", "type": "python3-function", "z": "a5557543ccff5889", "name": "download files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str, save\n\n\nscope = msg['scope']\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\n\nif msg['payload'] != 'YES':\n return\n\ncounter = 0\n\nfor i in msg[scope]:\n if msg[scope][i]['update'] == False:\n continue\n \n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n \n r = requests.get(url + msg[scope][i]['src'])\n if r.status_code != 200:\n msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\n return msg\n with open(temp, 'wb+') as file:\n file.write(r.content)\n shutil.copy(temp, msg[scope][i]['dst'])\n \n if msg[scope][i]['dst'] == '/boot/config.txt':\n save('camera','')\n \n counter += 1\n\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\n\nif counter == msg['counter']:\n updatepath_temp = updatepath + 'update_temp.json'\n updatepath_old = updatepath + 'update.json'\n shutil.move(updatepath_temp, updatepath_old)\n\nreturn msg\n", "outputs": 1, "x": 880, "y": 560, "wires": [ [ "42061b28cff81f99", "fe3a855fee9e28c6" ] ] }, { "id": "929281fef53e09f8", "type": "function", "z": "a5557543ccff5889", "name": "msg", "func": "if (msg.payload == 'YES'){\n msg.status = 'Installing updates'\n return msg}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 850, "y": 520, "wires": [ [ "42061b28cff81f99" ] ] }, { "id": "42061b28cff81f99", "type": "link out", "z": "a5557543ccff5889", "name": "", "mode": "link", "links": [ "cbd0afc4aa7b302a" ], "x": 995, "y": 520, "wires": [] }, { "id": "49f1ecb29a3f84f4", "type": "function", "z": "a5557543ccff5889", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\n\nmsg.payload = data\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 210, "y": 520, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "fe3a855fee9e28c6", "type": "link out", "z": "a5557543ccff5889", "name": "", "mode": "link", "links": [ "9bb0adbd716ce347", "01c882fcc51b349c" ], "x": 995, "y": 560, "wires": [] }, { "id": "5e7d5e4335d37794", "type": "link in", "z": "a5557543ccff5889", "name": "", "links": [ "960912e90ba5b5bc", "50eeb3e362f9027f" ], "x": 95, "y": 700, "wires": [ [ "2bb5fe78e09fec8a" ] ] }, { "id": "2bb5fe78e09fec8a", "type": "python3-function", "z": "a5557543ccff5889", "name": "msg", "func": "\nfrom subprocess import getoutput\nimport os\n\nmsg['os'] = getoutput(\"cat /etc/os-release | grep -i 'PRETTY_NAME'\")[13:-1]\nmsg['device'] = getoutput(\"cat /proc/device-tree/model\")\nmsg['flask'] = getoutput(\"systemctl status flask |grep -i 'Active:'\").split(' ')[6]\nmsg['osdate'] = getoutput(\"vcgencmd version\").split('\\n')[0]\nmsg['temp'] = getoutput(\"vcgencmd measure_temp\").split('=')[1]\ncpu_total = getoutput(\"free -m | head -n2 | tail -n1 |awk '{print $2}'\")\ncpu_used = getoutput(\"free -m | head -n2 | tail -n1 |awk '{print $3}'\")\nswap_total = getoutput(\"free -m | head -n3 | tail -n1 |awk '{print $2}'\")\nswap_used = getoutput(\"free -m | head -n3 | tail -n1 |awk '{print $3}'\")\ndiskspace_used = getoutput(\"df -h / | tail -n1 |awk '{print $3}'\")\ndiskspace_total = getoutput(\"df -h / | tail -n1 |awk '{print $2}'\")\n\nmsg['cpu'] = cpu_used + '/' + cpu_total + 'MB'\nmsg['swap'] = swap_used + '/' + swap_total + 'MB'\nmsg['diskspace'] =diskspace_used + '/' + diskspace_total\n\nif msg['flask'] == 'inactive':\n os.system('systemctl restart flask')\n\nreturn msg", "outputs": 1, "x": 210, "y": 700, "wires": [ [ "dbc77052ac950624", "d97c3068ef5fef96", "73a3b828f862312b", "901e31453b2bdff8", "f983854748ee4763", "5347c7c517f5e8c7", "3a5016f7003cd72c", "6d720c4a4ecd9475", "6438b7d060a70d81" ] ] }, { "id": "d97c3068ef5fef96", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 2, "width": 0, "height": 0, "name": "", "label": "OS:", "format": "{{msg.os}}", "layout": "row-spread", "className": "", "x": 490, "y": 740, "wires": [] }, { "id": "73a3b828f862312b", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 8, "width": 0, "height": 0, "name": "", "label": "Flask:", "format": "{{msg.flask}}", "layout": "row-spread", "className": "", "x": 490, "y": 780, "wires": [] }, { "id": "dbc77052ac950624", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 1, "width": 0, "height": 0, "name": "", "label": "Device:", "format": "{{msg.device}}", "layout": "row-spread", "className": "", "x": 500, "y": 700, "wires": [] }, { "id": "3f42560297fe6978", "type": "ui_template", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "name": "Download LOG", "order": 9, "width": 6, "height": 1, "format": "\n
Download error log\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 180, "y": 1060, "wires": [ [] ] }, { "id": "c94623ddd9d95f78", "type": "python3-function", "z": "a5557543ccff5889", "name": "get update", "func": "from OpenScan import save\n\nif msg['status'] == \"No new update available\":\n save('updateable',False)\nelif msg['status'] == \"New update available\":\n save('updateable',True)\n", "outputs": 1, "x": 210, "y": 360, "wires": [ [] ] }, { "id": "39a502b38837273d", "type": "link in", "z": "a5557543ccff5889", "name": "", "links": [ "1e7457ea9c2c5e09" ], "x": 245, "y": 600, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "901e31453b2bdff8", "type": "delay", "z": "a5557543ccff5889", "name": "", "pauseType": "delay", "timeout": "10", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 220, "y": 740, "wires": [ [ "2bb5fe78e09fec8a" ] ] }, { "id": "f983854748ee4763", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 3, "width": 0, "height": 0, "name": "", "label": "", "format": "{{msg.osdate}}", "layout": "row-spread", "className": "", "x": 490, "y": 820, "wires": [] }, { "id": "5347c7c517f5e8c7", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 4, "width": 0, "height": 0, "name": "", "label": "CPU temp:", "format": "{{msg.temp}}", "layout": "row-spread", "className": "", "x": 510, "y": 860, "wires": [] }, { "id": "3a5016f7003cd72c", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 5, "width": 0, "height": 0, "name": "", "label": "CPU memory:", "format": "{{msg.cpu}}", "layout": "row-spread", "className": "", "x": 520, "y": 900, "wires": [] }, { "id": "6d720c4a4ecd9475", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 6, "width": 0, "height": 0, "name": "", "label": "Swap memory:", "format": "{{msg.swap}}", "layout": "row-spread", "className": "", "x": 520, "y": 940, "wires": [] }, { "id": "6438b7d060a70d81", "type": "ui_text", "z": "a5557543ccff5889", "group": "3ce32450.e0cffc", "order": 7, "width": 0, "height": 0, "name": "", "label": "Diskspace:", "format": "{{msg.diskspace}}", "layout": "row-spread", "className": "", "x": 510, "y": 980, "wires": [] }, { "id": "8d012912f302be85", "type": "ui_button", "z": "a5557543ccff5889", "name": "", "group": "ddbd496e.93a288", "order": 8, "width": 6, "height": 1, "passthru": false, "label": "Show Details/Changelog", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 210, "y": 640, "wires": [ [ "5242607a723cc628" ] ] }, { "id": "5242607a723cc628", "type": "python3-function", "z": "a5557543ccff5889", "name": "Changelog", "func": "import requests\n\ntempfile = '/home/pi/OpenScan/tmp/changelog'\n\nurl = 'https://raw.githubusercontent.com/OpenScan-org/OpenScan-Doc/main/docs/changelog.md'\nr = requests.get(url, allow_redirects=False)\n\nwith open(tempfile,'wb') as file:\n file.write(r.content)\n \nwith open(tempfile, 'r') as file:\n text = file.read()\n \ntext = text.replace('\\n','
').replace('*', '  - ')\nmsg['payload'] = text\n\nreturn msg", "outputs": 1, "x": 430, "y": 640, "wires": [ [ "573722197b15bf84" ] ] }, { "id": "573722197b15bf84", "type": "ui_toast", "z": "a5557543ccff5889", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": true, "className": "", "topic": "", "name": "", "x": 610, "y": 640, "wires": [ [] ] } ] ================================================ FILE: update/betaArdu/settings.js ================================================ /** * Node-RED Settings created at Thu, 20 Apr 2023 08:41:18 GMT * * It can contain any valid JavaScript code that will get run when Node-RED * is started. * * Lines that start with // are commented out. * Each entry should be separated from the entries above and below by a comma ',' * * For more information about individual settings, refer to the documentation: * https://nodered.org/docs/user-guide/runtime/configuration * * The settings are split into the following sections: * - Flow File and User Directory Settings * - Security * - Server Settings * - Runtime Settings * - Editor Settings * - Node Settings * **/ module.exports = { /******************************************************************************* * Flow File and User Directory Settings * - flowFile * - credentialSecret * - flowFilePretty * - userDir * - nodesDir ******************************************************************************/ /** The file containing the flows. If not set, defaults to flows_.json **/ flowFile: "flows.json", /** By default, credentials are encrypted in storage using a generated key. To * specify your own secret, set the following property. * If you want to disable encryption of credentials, set this property to false. * Note: once you set this property, do not change it - doing so will prevent * node-red from being able to decrypt your existing credentials and they will be * lost. */ credentialSecret: false, /** By default, the flow JSON will be formatted over multiple lines making * it easier to compare changes when using version control. * To disable pretty-printing of the JSON set the following property to false. */ flowFilePretty: true, /** By default, all user data is stored in a directory called `.node-red` under * the user's home directory. To use a different location, the following * property can be used */ //userDir: '/home/nol/.node-red/', userDir: '/home/pi/OpenScan/settings/.node-red/', /** Node-RED scans the `nodes` directory in the userDir to find local node files. * The following property can be used to specify an additional directory to scan. */ //nodesDir: '/home/nol/.node-red/nodes', /******************************************************************************* * Security * - adminAuth * - https * - httpsRefreshInterval * - requireHttps * - httpNodeAuth * - httpStaticAuth ******************************************************************************/ /** To password protect the Node-RED editor and admin API, the following * property can be used. See http://nodered.org/docs/security.html for details. */ //adminAuth: { // type: "credentials", // users: [{ // username: "admin", // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", // permissions: "*" // }] //}, /** The following property can be used to enable HTTPS * This property can be either an object, containing both a (private) key * and a (public) certificate, or a function that returns such an object. * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener * for details of its contents. */ /** Option 1: static object */ //https: { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') //}, /** Option 2: function that returns the HTTP configuration object */ // https: function() { // // This function should return the options object, or a Promise // // that resolves to the options object // return { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') // } // }, /** If the `https` setting is a function, the following setting can be used * to set how often, in hours, the function will be called. That can be used * to refresh any certificates. */ //httpsRefreshInterval : 12, /** The following property can be used to cause insecure HTTP connections to * be redirected to HTTPS. */ //requireHttps: true, /** To password protect the node-defined HTTP endpoints (httpNodeRoot), * including node-red-dashboard, or the static content (httpStatic), the * following properties can be used. * The `pass` field is a bcrypt hash of the password. * See http://nodered.org/docs/security.html#generating-the-password-hash */ //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, /******************************************************************************* * Server Settings * - uiPort * - uiHost * - apiMaxLength * - httpServerOptions * - httpAdminRoot * - httpAdminMiddleware * - httpNodeRoot * - httpNodeCors * - httpNodeMiddleware * - httpStatic * - httpStaticRoot ******************************************************************************/ /** the tcp port that the Node-RED web server is listening on */ uiPort: process.env.PORT || 80, /** By default, the Node-RED UI accepts connections on all IPv4 interfaces. * To listen on all IPv6 addresses, set uiHost to "::", * The following property can be used to listen on a specific interface. For * example, the following would only allow connections from the local machine. */ //uiHost: "127.0.0.1", /** The maximum size of HTTP request that will be accepted by the runtime api. * Default: 5mb */ //apiMaxLength: '5mb', /** The following property can be used to pass custom options to the Express.js * server used by Node-RED. For a full list of available options, refer * to http://expressjs.com/en/api.html#app.settings.table */ //httpServerOptions: { }, /** By default, the Node-RED UI is available at http://localhost:1880/ * The following property can be used to specify a different root path. * If set to false, this is disabled. */ httpAdminRoot: '/editor', /** The following property can be used to add a custom middleware function * in front of all admin http routes. For example, to set custom http * headers. It can be a single function or an array of middleware functions. */ // httpAdminMiddleware: function(req,res,next) { // // Set the X-Frame-Options header to limit where the editor // // can be embedded // //res.set('X-Frame-Options', 'sameorigin'); // next(); // }, /** Some nodes, such as HTTP In, can be used to listen for incoming http requests. * By default, these are served relative to '/'. The following property * can be used to specifiy a different root path. If set to false, this is * disabled. */ //httpNodeRoot: '/red-nodes', /** The following property can be used to configure cross-origin resource sharing * in the HTTP nodes. * See https://github.com/troygoode/node-cors#configuration-options for * details on its contents. The following is a basic permissive set of options: */ //httpNodeCors: { // origin: "*", // methods: "GET,PUT,POST,DELETE" //}, /** If you need to set an http proxy please set an environment variable * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. * For example - http_proxy=http://myproxy.com:8080 * (Setting it here will have no effect) * You may also specify no_proxy (or NO_PROXY) to supply a comma separated * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk */ /** The following property can be used to add a custom middleware function * in front of all http in nodes. This allows custom authentication to be * applied to all http in nodes, or any other sort of common request processing. * It can be a single function or an array of middleware functions. */ //httpNodeMiddleware: function(req,res,next) { // // Handle/reject the request, or pass it on to the http in node by calling next(); // // Optionally skip our rawBodyParser by setting this to true; // //req.skipRawBodyParser = true; // next(); //}, /** When httpAdminRoot is used to move the UI to a different root path, the * following property can be used to identify a directory of static content * that should be served at http://localhost:1880/. * When httpStaticRoot is set differently to httpAdminRoot, there is no need * to move httpAdminRoot */ httpStatic: '/home/pi/OpenScan/', //httpStatic: '/home/nol/node-red-static/', //single static source /* OR multiple static sources can be created using an array of objects... */ //httpStatic: [ // {path: '/home/nol/pics/', root: "/img/"}, // {path: '/home/nol/reports/', root: "/doc/"}, //], /** * All static routes will be appended to httpStaticRoot * e.g. if httpStatic = "/home/nol/docs" and httpStaticRoot = "/static/" * then "/home/nol/docs" will be served at "/static/" * e.g. if httpStatic = [{path: '/home/nol/pics/', root: "/img/"}] * and httpStaticRoot = "/static/" * then "/home/nol/pics/" will be served at "/static/img/" */ //httpStaticRoot: '/static/', /******************************************************************************* * Runtime Settings * - lang * - logging * - contextStorage * - exportGlobalContextKeys * - externalModules ******************************************************************************/ /** Uncomment the following to run node-red in your preferred language. * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko * Some languages are more complete than others. */ // lang: "de", /** Configure the logging output */ logging: { /** Only console logging is currently supported */ console: { /** Level of logging to be recorded. Options are: * fatal - only those errors which make the application unusable should be recorded * error - record errors which are deemed fatal for a particular request + fatal errors * warn - record problems which are non fatal + errors + fatal errors * info - record information about the general running of the application + warn + error + fatal errors * debug - record information which is more verbose than info + info + warn + error + fatal errors * trace - record very detailed logging + debug + info + warn + error + fatal errors * off - turn off all logging (doesn't affect metrics or audit) */ level: "info", /** Whether or not to include metric events in the log output */ metrics: false, /** Whether or not to include audit events in the log output */ audit: false } }, /** Context Storage * The following property can be used to enable context storage. The configuration * provided here will enable file-based context that flushes to disk every 30 seconds. * Refer to the documentation for further options: https://nodered.org/docs/api/context/ */ //contextStorage: { // default: { // module:"localfilesystem" // }, //}, /** `global.keys()` returns a list of all properties set in global context. * This allows them to be displayed in the Context Sidebar within the editor. * In some circumstances it is not desirable to expose them to the editor. The * following property can be used to hide any property set in `functionGlobalContext` * from being list by `global.keys()`. * By default, the property is set to false to avoid accidental exposure of * their values. Setting this to true will cause the keys to be listed. */ exportGlobalContextKeys: false, /** Configure how the runtime will handle external npm modules. * This covers: * - whether the editor will allow new node modules to be installed * - whether nodes, such as the Function node are allowed to have their * own dynamically configured dependencies. * The allow/denyList options can be used to limit what modules the runtime * will install/load. It can use '*' as a wildcard that matches anything. */ externalModules: { // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ // palette: { /** Configuration for the Palette Manager */ // allowInstall: true, /** Enable the Palette Manager in the editor */ // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ // allowList: [], // denyList: [] // }, // modules: { /** Configuration for node-specified modules */ // allowInstall: true, // allowList: [], // denyList: [] // } }, /******************************************************************************* * Editor Settings * - disableEditor * - editorTheme ******************************************************************************/ /** The following property can be used to disable the editor. The admin API * is not affected by this option. To disable both the editor and the admin * API, use either the httpRoot or httpAdminRoot properties */ //disableEditor: false, /** Customising the editor * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes * for all available options. */ editorTheme: { /** The following property can be used to set a custom theme for the editor. * See https://github.com/node-red-contrib-themes/theme-collection for * a collection of themes to chose from. */ //theme: "", palette: { /** The following property can be used to order the categories in the editor * palette. If a node's category is not in the list, the category will get * added to the end of the palette. * If not set, the following default order is used: */ //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], }, projects: { /** To enable the Projects feature, set this value to true */ enabled: false, workflow: { /** Set the default projects workflow mode. * - manual - you must manually commit changes * - auto - changes are automatically committed * This can be overridden per-user from the 'Git config' * section of 'User Settings' within the editor */ mode: "manual" } }, codeEditor: { /** Select the text editor component used by the editor. * As of Node-RED V3, this defaults to "monaco", but can be set to "ace" if desired */ lib: "monaco", options: { /** The follow options only apply if the editor is set to "monaco" * * theme - must match the file name of a theme in * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme" */ theme: "vs", /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc. * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html */ //fontSize: 14, //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", //fontLigatures: true, } } }, /******************************************************************************* * Node Settings * - fileWorkingDirectory * - functionGlobalContext * - functionExternalModules * - nodeMessageBufferMaxLength * - ui (for use with Node-RED Dashboard) * - debugUseColors * - debugMaxLength * - execMaxBufferSize * - httpRequestTimeout * - mqttReconnectTime * - serialReconnectTime * - socketReconnectTime * - socketTimeout * - tcpMsgQueueSize * - inboundWebSocketTimeout * - tlsConfigDisableLocalFiles * - webSocketNodeVerifyClient ******************************************************************************/ /** The working directory to handle relative file paths from within the File nodes * defaults to the working directory of the Node-RED process. */ //fileWorkingDirectory: "", /** Allow the Function node to load additional npm modules directly */ functionExternalModules: true, /** The following property can be used to set predefined values in Global Context. * This allows extra node modules to be made available with in Function node. * For example, the following: * functionGlobalContext: { os:require('os') } * will allow the `os` module to be accessed in a Function node using: * global.get("os") */ // functionGlobalContext: { // os:require('os'), // }, functionGlobalContext: { // enables and pre-populates the context.global variable os:require('os'), path:require('path'), fs:require('fs') }, /** The maximum number of messages nodes will buffer internally as part of their * operation. This applies across a range of nodes that operate on message sequences. * defaults to no limit. A value of 0 also means no limit is applied. */ //nodeMessageBufferMaxLength: 0, /** If you installed the optional node-red-dashboard you can set it's path * relative to httpNodeRoot * Other optional properties include * readOnly:{boolean}, * middleware:{function or array}, (req,res,next) - http middleware * ioMiddleware:{function or array}, (socket,next) - socket.io middleware */ ui: { path: "" }, /** Colourise the console output of the debug node */ //debugUseColors: true, /** The maximum length, in characters, of any message sent to the debug sidebar tab */ debugMaxLength: 1000, /** Maximum buffer size for the exec node. Defaults to 10Mb */ //execMaxBufferSize: 10000000, /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */ //httpRequestTimeout: 120000, /** Retry time in milliseconds for MQTT connections */ mqttReconnectTime: 15000, /** Retry time in milliseconds for Serial port connections */ serialReconnectTime: 15000, /** Retry time in milliseconds for TCP socket connections */ //socketReconnectTime: 10000, /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */ //socketTimeout: 120000, /** Maximum number of messages to wait in queue while attempting to connect to TCP socket * defaults to 1000 */ //tcpMsgQueueSize: 2000, /** Timeout in milliseconds for inbound WebSocket connections that do not * match any configured node. Defaults to 5000 */ //inboundWebSocketTimeout: 5000, /** To disable the option for using local files for storing keys and * certificates in the TLS configuration node, set this to true. */ //tlsConfigDisableLocalFiles: true, /** The following property can be used to verify websocket connection attempts. * This allows, for example, the HTTP request headers to be checked to ensure * they include valid authentication information. */ //webSocketNodeVerifyClient: function(info) { // /** 'info' has three properties: // * - origin : the value in the Origin header // * - req : the HTTP request // * - secure : true if req.connection.authorized or req.connection.encrypted is set // * // * The function should return true if the connection should be accepted, false otherwise. // * // * Alternatively, if this function is defined to accept a second argument, callback, // * it can be used to verify the client asynchronously. // * The callback takes three arguments: // * - result : boolean, whether to accept the connection or not // * - code : if result is false, the HTTP error status to return // * - reason: if result is false, the HTTP reason string to return // */ //}, } ================================================ FILE: update/main/Arducam.py ================================================ import time import os try: import v4l2 except Exception as e: print(e) print("Try to install v4l2-fix") try: from pip import main as pipmain except ImportError: from pip._internal import main as pipmain pipmain(['install', 'v4l2-fix']) print("\nTry to run the focus program again.") exit(0) import fcntl import errno # # Type # v4l2.V4L2_CTRL_TYPE_INTEGER # v4l2.V4L2_CTRL_TYPE_BOOLEAN # v4l2.V4L2_CTRL_TYPE_MENU # v4l2.V4L2_CTRL_TYPE_BUTTON # v4l2.V4L2_CTRL_TYPE_INTEGER64 # v4l2.V4L2_CTRL_TYPE_CTRL_CLASS # # Flags # v4l2.V4L2_CTRL_FLAG_DISABLED # v4l2.V4L2_CTRL_FLAG_GRABBED # v4l2.V4L2_CTRL_FLAG_READ_ONLY # v4l2.V4L2_CTRL_FLAG_UPDATE # v4l2.V4L2_CTRL_FLAG_INACTIVE # v4l2.V4L2_CTRL_FLAG_SLIDER def assert_valid_queryctrl(queryctrl): return queryctrl.type & ( v4l2.V4L2_CTRL_TYPE_INTEGER | v4l2.V4L2_CTRL_TYPE_BOOLEAN | v4l2.V4L2_CTRL_TYPE_MENU | v4l2.V4L2_CTRL_TYPE_BUTTON | v4l2.V4L2_CTRL_TYPE_INTEGER64 | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS | 7 | 8 | 9 ) and queryctrl.flags & ( v4l2.V4L2_CTRL_FLAG_DISABLED | v4l2.V4L2_CTRL_FLAG_GRABBED | v4l2.V4L2_CTRL_FLAG_READ_ONLY | v4l2.V4L2_CTRL_FLAG_UPDATE | v4l2.V4L2_CTRL_FLAG_INACTIVE | v4l2.V4L2_CTRL_FLAG_SLIDER ) def get_device_controls_menu(fd, queryctrl): querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum) while querymenu.index <= queryctrl.maximum: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu) yield querymenu querymenu.index += 1 def get_device_controls_by_class(fd, control_class): # enumeration by control class queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) while True: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) except IOError as e: assert e.errno == errno.EINVAL break if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class: break yield queryctrl queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) def getdict(struct): val = dict((field, getattr(struct, field)) for field, _ in struct._fields_) val.pop("reserved") return val def get_device_controls(fd): # original enumeration method queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE) while queryctrl.id < v4l2.V4L2_CID_LASTP1: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) print(queryctrl.name) except IOError as e: # this predefined control is not supported by this device assert e.errno == errno.EINVAL queryctrl.id += 1 continue queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1) def get_ctrls(vd): ctrls = [] # enumeration by control class for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA): for queryctrl in get_device_controls_by_class(vd, class_): ctrl = getdict(queryctrl) if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): # print(querymenu.name) ctrl["menu"].append(querymenu.name) if queryctrl.type == 9: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): ctrl["menu"].append(querymenu.index) ctrls.append(ctrl) return ctrls def set_ctrl(vd, id, value): ctrl = v4l2.v4l2_control() ctrl.id = id ctrl.value = value try: fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl) except IOError as e: print(e) def get_ctrl(vd, id): ctrl = v4l2.v4l2_control() ctrl.id = id try: fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl) except IOError as e: print(e) return None return ctrl.value class Focuser: FOCUS_ID = 0x009a090a dev = None def __init__(self, dev=0): self.focus_value = 0 self.dev = dev if type(dev) == int or (type(dev) == str and dev.isnumeric()): self.dev = "/dev/video{}".format(dev) self.fd = open(self.dev, 'r') self.ctrls = get_ctrls(self.fd) self.hasFocus = False for ctrl in self.ctrls: if ctrl['id'] == Focuser.FOCUS_ID: self.hasFocus = True self.opts[Focuser.OPT_FOCUS]["MIN_VALUE"] = ctrl['minimum'] self.opts[Focuser.OPT_FOCUS]["MAX_VALUE"] = ctrl['maximum'] self.opts[Focuser.OPT_FOCUS]["DEF_VALUE"] = ctrl['default'] self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID) if not self.hasFocus: raise RuntimeError("Device {} has no focus_absolute control.".format(self.dev)) def read(self): return self.focus_value def write(self, value): self.focus_value = value # os.system("v4l2-ctl -d {} -c focus_absolute={}".format(self.dev, value)) set_ctrl(self.fd, Focuser.FOCUS_ID, value) OPT_BASE = 0x1000 OPT_FOCUS = OPT_BASE | 0x01 OPT_ZOOM = OPT_BASE | 0x02 OPT_MOTOR_X = OPT_BASE | 0x03 OPT_MOTOR_Y = OPT_BASE | 0x04 OPT_IRCUT = OPT_BASE | 0x05 opts = { OPT_FOCUS : { "MIN_VALUE": 0, "MAX_VALUE": 1000, "DEF_VALUE": 0, }, } def reset(self,opt,flag = 1): info = self.opts[opt] if info == None or info["DEF_VALUE"] == None: return self.set(opt,info["DEF_VALUE"]) def get(self,opt,flag = 0): info = self.opts[opt] return self.read() def set(self,opt,value,flag = 1): info = self.opts[opt] if value > info["MAX_VALUE"]: value = info["MAX_VALUE"] elif value < info["MIN_VALUE"]: value = info["MIN_VALUE"] self.write(value) print("write: {}".format(value)) def __del__(self): self.fd.close() pass ================================================ FILE: update/main/OpenScan.py ================================================ basepath = '/home/pi/OpenScan/' from os.path import isfile def load_bool(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') if value == '1' or value == 'True' or value =='true': value = True else: value = False return value def load_str(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') return value def load_int(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = int(file.read().replace('\n','')) return value def load_float(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = float(file.read().replace('\n','')) return value def save(name, value): filename = basepath+'settings/'+name with open(filename, 'w+') as file: file.write(str(value)) return def OpenScanCloud(cmd, msg): from requests import get osc_user = 'openscan' osc_pw = 'free' osc_server = 'http://openscanfeedback.dnsuser.de:1334/' try: r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg) except: r = type('obj', (object,), {'status_code' : 404, 'text':None}) return r def camera(cmd, msg = {}): from requests import get flask = 'http://127.0.0.1:1312/' try: r = get(flask + cmd, params=msg) return r.status_code except: return 400 def motorrun(motor,angle): import RPi.GPIO as GPIO from time import sleep from math import cos msg = {'cmd':'set'} camera('/ping', msg) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) spr = load_int(motor + '_stepsperrotation') dirpin = load_int('pin_' + motor + '_dir') steppin = load_int('pin_' + motor +'_step') dir = load_int(motor + '_dir') ramp = load_int(motor + '_accramp') acc = load_float(motor + '_acc') delay_init = load_float(motor + '_delay') delay = delay_init step_count=int(angle*spr/360) * dir GPIO.setup(dirpin, GPIO.OUT) GPIO.setup(steppin, GPIO.OUT) if (step_count>0): GPIO.output(dirpin, GPIO.HIGH) if(step_count<0): GPIO.output(dirpin, GPIO.LOW) step_count=-step_count for x in range(step_count): GPIO.output(steppin, GPIO.HIGH) if x<=ramp and x<=step_count/2: delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc) #delay=delay_init+(ramp-x)*(delay_init)/acc elif step_count-x<=ramp and x>step_count/2: delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc) #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc else: delay = delay_init sleep(delay) GPIO.output(steppin, GPIO.LOW) sleep(delay) def ringlight(number,state): import RPi.GPIO as GPIO msg = {'cmd':'set'} camera('/ping', msg) pin = load_int('pin_ringlight' + str(number)) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, state) def take_photo(file): from os import system filepath = basepath + file model=load_str('model') shutter = str(load_int('cam_shutter')) saturation = load_str('cam_saturation') contrast = load_str('cam_contrast') awbg_red = load_str('cam_awbg_red') awbg_blue = load_str('cam_awbg_blue') gain = load_str('cam_gain') quality = load_int('cam_jpeg_quality') filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg' #width = load_str('cam_resx') #height = load_str('cam_resy') timeout = load_str('cam_timeout') cropx = load_int('cam_cropx')/200 cropy = load_int('cam_cropy')/200 rotation = load_int('cam_rotation') AF = load_bool('cam_AFmode') camera = load_str('camera') if camera == 'imx519' and AF == True: autofocus = ' --autofocus ' else: autofocus = '' if camera == "usb_webcam": cmd = 'fswebcam -i 0 -r "1280x720" -F 5 --no-banner --jpeg 95 --save ' + filepath2 else: cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1' # cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus system(cmd) return cmd def get_points(samples=1): from math import pi, sqrt, acos, atan2, cos, sin points = [] phi = pi * (3. - sqrt(5.)) for i in range(int(samples)): y = 1 - (i / float(samples - 1)) * 2 radius = sqrt(1 - y * y) theta = phi * i x = cos(theta) * radius z = sin(theta) * radius r=sqrt(x*x+y*y+z*z) theta_neu=acos(z/r)*180/pi phi_neu=atan2(y,x)*180/pi points.append((theta_neu-90,phi_neu)) points.sort() return points def create_coordinates(angle_min, angle_max,point_count): point_count_final=point_count if angle_max < angle_min: a = angle_min angle_min = angle_max angle_max = a point_count=point_count*90/(angle_max-angle_min) actual_points=0 while actual_pointsangle_min and x20: point_count=point_count+3 else: point_count=point_count+1 return filtered ================================================ FILE: update/main/config.txt ================================================ # For more options and information see # http://rpf.io/configtxt # Some settings may impact device functionality. See link above for details # uncomment if you get no picture on HDMI for a default "safe" mode #hdmi_safe=1 hdmi_blanking=2 # uncomment the following to adjust overscan. Use positive numbers if console # goes off screen, and negative if there is too much border #overscan_left=16 #overscan_right=16 #overscan_top=16 #overscan_bottom=16 # uncomment to force a console size. By default it will be display's size minus # overscan. #framebuffer_width=1280 #framebuffer_height=720 # uncomment if hdmi display is not detected and composite is being output #hdmi_force_hotplug=1 # uncomment to force a specific HDMI mode (this will force VGA) #hdmi_group=1 #hdmi_mode=1 # uncomment to force a HDMI mode rather than DVI. This can make audio work in # DMT (computer monitor) modes #hdmi_drive=2 # uncomment to increase signal to HDMI, if you have interference, blanking, or # no display #config_hdmi_boost=4 # uncomment for composite PAL #sdtv_mode=2 #uncomment to overclock the arm. 700 MHz is the default. #arm_freq=800 # Uncomment some or all of these to enable the optional hardware interfaces #dtparam=i2c_arm=on #dtparam=i2s=on #dtparam=spi=on # Uncomment this to enable infrared communication. #dtoverlay=gpio-ir,gpio_pin=17 #dtoverlay=gpio-ir-tx,gpio_pin=18 # Additional overlays and parameters are documented /boot/overlays/README # Enable audio (loads snd_bcm2835) dtparam=audio=on # Automatically load overlays for detected cameras camera_auto_detect=0 # Automatically load overlays for detected DSI displays display_auto_detect=1 # Enable DRM VC4 V3D driver #dtoverlay=vc4-kms-v3d max_framebuffers=2 # Disable compensation for displays with overscan disable_overscan=1 [cm4] # Enable host mode on the 2711 built-in XHCI USB controller. # This line should be removed if the legacy DWC2 controller is required # (e.g. for USB device mode) or if USB support is not required. otg_mode=1 [pi4] # Run as fast as firmware / board allows arm_boost=1 [all] camera_auto_detect=0 gpu_mem=256 dtoverlay=vc4-fkms-v3d dtoverlay=imx519,media-controller=1 ================================================ FILE: update/main/fla.py ================================================ from flask import Flask, make_response, jsonify, request, abort from PIL import Image import gphoto2 as gp from time import sleep, time import shutil from OpenScan import load_int, load_str, load_float, load_bool, ringlight import RPi.GPIO as GPIO from math import sqrt import os GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) app = Flask(__name__) basedir = '/home/pi/OpenScan/' timer = time() ################################################################################################################### @app.route('/shutdown', methods=['get']) def shutdown(): delay = 0.2 ringlight(2, False) for i in range(5): ringlight(1, True) sleep(delay) ringlight(1, False) sleep(delay) os.system('shutdown -h now') return "Shutting down...", 200 ################################################################################################################### @app.route('/reboot', methods=['get']) def reboot(): delay = 0.2 ringlight(2, False) for i in range(5): ringlight(1, True) sleep(delay) ringlight(1, False) sleep(delay) os.system('reboot -h') return "Rebooting...", 200 ################################################################################################################### @app.route('/ping', methods=['get']) def ping(): global timer cmd = str(request.args.get('cmd')) if cmd == 'set': timer = time() inactive = time() - timer return ({'inactive':inactive}, 200) ################################################################################################################### @app.route('/gphoto_init', methods=['get']) def gphoto_init(): global camera camera = gp.Camera() camera.init() return ({}, 200) ################################################################################################################### @app.route('/gphoto_preview', methods=['get']) def gphoto_preview(): filepath = str(request.args.get('filepath')) camera_file = gp.gp_camera_capture_preview(camera)[1] target = basedir + filepath camera_file.save(target) return ({}, 200) ################################################################################################################### @app.route('/gphoto_capture', methods=['get']) def gphoto_capture(): filepath = str(request.args.get('filepath')) file_path = camera.capture(gp.GP_CAPTURE_IMAGE) camera_file = camera.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL) camera_file.save(basedir + filepath) return ({}, 200) ################################################################################################################### @app.route('/gphoto_test', methods=['get']) def gphoto_test(): text = camera.get_summary() return ({}, 200) ################################################################################################################### @app.route('/gphoto_exit', methods=['get']) def gphoto_exit(): global camera camera.exit() return ({}, 200) ################################################################################################################### @app.route('/crop', methods=['get']) def crop(): output_downscale = load_bool('cam_output_downscale') output_resolution = load_int('cam_output_resolution') preview_resolution = load_int('cam_preview_resolution') filepath_in = basedir + str(request.args.get('filepath_in')) filepath_out = basedir + str(request.args.get('filepath_out')) cropx = int(request.args.get('cropx'))/200 cropy = int(request.args.get('cropy'))/200 rotation = int(request.args.get('rotation')) preview = str(request.args.get('preview')) downscale = 1 with Image.open(filepath_in) as img: w,h = img.size if cropx != 0 or cropy != 0: img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy))) if rotation == 90: img = img.transpose(Image.ROTATE_90) elif rotation == 180: img= img.transpose(Image.ROTATE_180) elif rotation == 270: img= img.transpose(Image.ROTATE_270) if preview == "True": w,h = img.size factor = (w*h)/preview_resolution if factor > 1: img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS) elif output_downscale == True: w,h = img.size factor = (w*h)/output_resolution if factor > 1: img = img.resize((int(w/sqrt(factor)),int(h/sqrt(factor))),Image.ANTIALIAS) img.save(filepath_out, quality=95, subsampling=0) return ({}, 200) ################################################################################################################### @app.route('/external_capture', methods=['get']) def external_capture(): pin = load_int('pin_external') delay_before = load_float('cam_delay_before') timeout = load_float('cam_timeout')/1000 delay_after = load_float('cam_delay_after') GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, GPIO.LOW) sleep(delay_before) GPIO.output(pin, GPIO.HIGH) sleep(timeout) GPIO.output(pin, GPIO.LOW) sleep(delay_after) return ({}, 200) if __name__ == '__main__': # app.run(host='127.0.0.1', port=1312, debug=False, threaded=True) app.run(host='0.0.0.0', port=1312, debug=False, threaded=True) ================================================ FILE: update/main/flows.json ================================================ [ { "id": "829d803b6033a693", "type": "tab", "label": "HOME", "disabled": false, "info": "", "env": [] }, { "id": "1613373abaf77a2c", "type": "tab", "label": "SCAN", "disabled": false, "info": "", "env": [] }, { "id": "4981d84ef1a366d1", "type": "tab", "label": "Files&Cloud", "disabled": false, "info": "", "env": [] }, { "id": "017bd4e4a428bee5", "type": "tab", "label": "SETTINGS", "disabled": false, "info": "", "env": [] }, { "id": "c8e7ecb5849edb9a", "type": "tab", "label": "UPDATE", "disabled": false, "info": "", "env": [] }, { "id": "b3150b13e34b1fe8", "type": "ui_tab", "name": "OpenScan", "icon": "dashboard", "order": 1, "disabled": false, "hidden": false }, { "id": "b6e9c2df6b28ff66", "type": "ui_base", "theme": { "name": "theme-dark", "lightTheme": { "default": "#0094CE", "baseColor": "#0094CE", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "darkTheme": { "default": "#097479", "baseColor": "#097479", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "customTheme": { "name": "Untitled Theme 1", "default": "#4B7930", "baseColor": "#4B7930", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" }, "themeState": { "base-color": { "default": "#097479", "value": "#097479", "edited": false }, "page-titlebar-backgroundColor": { "value": "#097479", "edited": false }, "page-backgroundColor": { "value": "#111111", "edited": false }, "page-sidebar-backgroundColor": { "value": "#333333", "edited": false }, "group-textColor": { "value": "#0eb8c0", "edited": false }, "group-borderColor": { "value": "#555555", "edited": false }, "group-backgroundColor": { "value": "#333333", "edited": false }, "widget-textColor": { "value": "#eeeeee", "edited": false }, "widget-backgroundColor": { "value": "#097479", "edited": false }, "widget-borderColor": { "value": "#333333", "edited": false }, "base-font": { "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" } }, "angularTheme": { "primary": "indigo", "accents": "blue", "warn": "red", "background": "grey", "palette": "light" } }, "site": { "name": "OpenScan 3D Scanner", "hideToolbar": "false", "allowSwipe": "false", "lockMenu": "false", "allowTempTheme": "true", "dateFormat": "DD/MM/YYYY", "sizes": { "sx": 46, "sy": 46, "gx": 10, "gy": 10, "cx": 6, "cy": 6, "px": 6, "py": 6 } } }, { "id": "729f9ea6e3513c9b", "type": "ui_group", "name": "Home", "tab": "b3150b13e34b1fe8", "order": 2, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "65ae49b64fa0d83e", "type": "ui_tab", "name": "Settings", "icon": "dashboard", "order": 4, "disabled": false, "hidden": false }, { "id": "4fe6b4c0ade0938a", "type": "ui_group", "name": "General", "tab": "65ae49b64fa0d83e", "order": 1, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "0fe66c9190b8a87c", "type": "ui_group", "name": "Network", "tab": "65ae49b64fa0d83e", "order": 2, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "93aadb71dee6d977", "type": "ui_group", "name": "Camera", "tab": "65ae49b64fa0d83e", "order": 4, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "d49a6dfd7fb17096", "type": "ui_group", "name": "Motor", "tab": "65ae49b64fa0d83e", "order": 5, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "644b3bcc903d46ca", "type": "ui_group", "name": "Pinout", "tab": "65ae49b64fa0d83e", "order": 6, "disp": true, "width": "6", "collapse": true, "className": "" }, { "id": "e23b837a9f040895", "type": "ui_tab", "name": "Scan", "icon": "dashboard", "order": 2, "disabled": false, "hidden": false }, { "id": "7aaf184330605300", "type": "ui_group", "name": "Settings", "tab": "e23b837a9f040895", "order": 1, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "ce9cc9d915dc6eb6", "type": "ui_group", "name": "Picamera", "tab": "e23b837a9f040895", "order": 2, "disp": false, "width": "12", "collapse": false, "className": "" }, { "id": "90223f7ddc082321", "type": "ui_group", "name": "Arducam", "tab": "e23b837a9f040895", "order": 3, "disp": false, "width": 12, "collapse": false, "className": "" }, { "id": "7625f9c9e8dbc5c6", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "", "order": 4, "width": 1, "height": 1 }, { "id": "3b4bd36726be16d5", "type": "ui_group", "name": "OpenScanCloud", "tab": "65ae49b64fa0d83e", "order": 3, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "b5fdd57b.15eda8", "type": "ui_group", "name": "Main", "tab": "15a222ed.d70a7d", "order": 1, "disp": false, "width": 13, "collapse": false }, { "id": "db43d646.2074c8", "type": "ui_group", "name": "OpenScanCloud", "tab": "15a222ed.d70a7d", "order": 2, "disp": true, "width": "6", "collapse": false }, { "id": "15a222ed.d70a7d", "type": "ui_tab", "name": "Files&Cloud", "icon": "dashboard", "order": 3, "disabled": false, "hidden": false }, { "id": "ddbd496e.93a288", "type": "ui_group", "name": "Manage Updates", "tab": "d25e08b4.5b27e8", "order": 1, "disp": true, "width": "6", "collapse": false }, { "id": "3ce32450.e0cffc", "type": "ui_group", "name": "System & Stats", "tab": "d25e08b4.5b27e8", "order": 2, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "d25e08b4.5b27e8", "type": "ui_tab", "name": "Update & Info", "icon": "dashboard", "order": 5, "disabled": false, "hidden": false }, { "id": "1f7f7e1e24f5ad9b", "type": "ui_group", "name": "Initialize", "tab": "b3150b13e34b1fe8", "order": 3, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "5b3e5aca21140e9a", "type": "ui_group", "name": "Update", "tab": "b3150b13e34b1fe8", "order": 1, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "3b4961c4e72ff58a", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "4fe6b4c0ade0938a", "order": 6, "width": 6, "height": 1 }, { "id": "5ef40dca2c6c6aab", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "4fe6b4c0ade0938a", "order": 11, "width": 6, "height": 1 }, { "id": "bdd26746cc1e1ba0", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "3b4bd36726be16d5", "order": 6, "width": 2, "height": 1 }, { "id": "3584b5ef2b7acb72", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "3b4bd36726be16d5", "order": 8, "width": 2, "height": 1 }, { "id": "cac67f0e.f01fa", "type": "ui_group", "name": "Button Top", "tab": "", "order": 1, "disp": true, "width": "6", "collapse": false }, { "id": "c178a6075244d901", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "729f9ea6e3513c9b", "order": 6, "width": 1, "height": 1 }, { "id": "f8d7cbc04f8cbcba", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "729f9ea6e3513c9b", "order": 8, "width": 1, "height": 1 }, { "id": "bed9a462261844dc", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "90223f7ddc082321", "order": 14, "width": 2, "height": 1 }, { "id": "42acb237087cf7f1", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "90223f7ddc082321", "order": 15, "width": 2, "height": 1 }, { "id": "dc9e2987583f2cbb", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "90223f7ddc082321", "order": 16, "width": 2, "height": 1 }, { "id": "3fe52603e2ac73b6", "type": "ui_template", "z": "829d803b6033a693", "group": "729f9ea6e3513c9b", "name": "Background", "order": 1, "width": 0, "height": 0, "format": "", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "global", "className": "", "x": 110, "y": 40, "wires": [ [] ] }, { "id": "4468f691.103eb8", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 2, "width": 3, "height": 2, "passthru": false, "label": "SCAN", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "1", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 100, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "6560dd25.9e76c4", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 4, "width": 3, "height": 2, "passthru": false, "label": "Settings", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "3", "payloadType": "num", "topic": "", "topicType": "str", "x": 100, "y": 180, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "62cd5288.2805fc", "type": "ui_ui_control", "z": "829d803b6033a693", "name": "", "events": "all", "x": 280, "y": 100, "wires": [ [] ] }, { "id": "71e72293.91c6fc", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 3, "width": 3, "height": 2, "passthru": false, "label": "Files", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "2", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 140, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "e7306ef2.3b4df", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 5, "width": 3, "height": 2, "passthru": false, "label": "Update&Info", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "4", "payloadType": "num", "topic": "", "topicType": "str", "x": 110, "y": 220, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "88edad7ca53698fd", "type": "inject", "z": "829d803b6033a693", "name": "1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "true", "payloadType": "bool", "x": 90, "y": 400, "wires": [ [ "000a811a215e08d4", "83c2b5ea51f0fec3", "88fde4ab78c965d7", "bee62d2a99cbc63b", "8e39e4a037487ecd", "bb84b9e5c7d8e21f", "7113d7b25a851151", "c4c1580c289fc7bd" ] ] }, { "id": "bd75f33b8a57c522", "type": "link out", "z": "829d803b6033a693", "name": "enable", "mode": "link", "links": [ "8367cfa0bf5bc5df", "92c98e6ce7cd25f9", "b33d604c.5f1a6" ], "x": 335, "y": 440, "wires": [] }, { "id": "000a811a215e08d4", "type": "function", "z": "829d803b6033a693", "name": "enable", "func": "msg.enabled = true\nmsg.payload = 1\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 440, "wires": [ [ "bd75f33b8a57c522" ] ] }, { "id": "83c2b5ea51f0fec3", "type": "function", "z": "829d803b6033a693", "name": "disable", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 240, "y": 480, "wires": [ [ "6b94bf2295b1b31d" ] ] }, { "id": "6b94bf2295b1b31d", "type": "link out", "z": "829d803b6033a693", "name": "disable", "mode": "link", "links": [ "a1d29e56599da0bd" ], "x": 335, "y": 480, "wires": [] }, { "id": "88fde4ab78c965d7", "type": "function", "z": "829d803b6033a693", "name": "write", "func": "var file = 'status_cloud'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\ncontent = 'ready'\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 520, "wires": [ [] ] }, { "id": "960912e90ba5b5bc", "type": "link out", "z": "829d803b6033a693", "name": "started1s", "mode": "link", "links": [ "2f4c0f98.dee2", "397ab7f44b893c89", "65145c939b6647e2", "65b38bfeb3fee710", "6d1e12f51f9af0b6", "788fabff98c7973c", "9b2bc9849aee310b", "a1e14624058e74cd", "a67c18aaca2f5fa5", "bd80ec228fb9a86d", "cc9c4092edeb43cc", "d3fc91d87d5d5f62", "d7c1fb4c028b21a5", "e5f38b4a07a5e278", "f0b355967b33dfee", "d0104e0163745993", "5e7d5e4335d37794" ], "x": 615, "y": 800, "wires": [] }, { "id": "168d72a54504b327", "type": "inject", "z": "829d803b6033a693", "name": "5/0.1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.1", "crontab": "", "once": true, "onceDelay": "5", "topic": "", "payload": "", "payloadType": "str", "x": 100, "y": 720, "wires": [ [ "6c6ef2255a7d39e5" ] ] }, { "id": "6c6ef2255a7d39e5", "type": "link out", "z": "829d803b6033a693", "name": "repeat 5s/0.1s", "mode": "link", "links": [ "61990987acd0f263", "2415272f42ce468c" ], "x": 195, "y": 720, "wires": [] }, { "id": "bee62d2a99cbc63b", "type": "function", "z": "829d803b6033a693", "name": "global", "func": "global.set('flag_pw', true)\nglobal.set('flag', true)\nglobal.set('combine', false)\nglobal.set('focus', 2838)\nglobal.set('focus1', 0)\nglobal.set('focus2', 0)\n\nglobal.set('focuser', true)\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 400, "wires": [ [ "f20da2fc4978b7bf" ] ] }, { "id": "544d20f02215011a", "type": "function", "z": "829d803b6033a693", "name": "CREATE FACTORY DEFAULT", "func": "msg = { \n'overwrite':msg.overwrite,\n'settings':\n {\n 'advanced_settings':false,\n 'cameras':{\n 'imx519':[4656,3496],\n 'imx219':[3280,2464],\n 'imx477':[4056,3040],\n 'ov5647':[2592,1944],\n 'imx378':[3840,2880],\n 'ov9271':[1280,800],\n 'imx290a':[1920,1080],\n 'imx290b':[1920,1080],\n },\n 'cam_AFmode':true,\n 'cam_STmode':true,\n 'cam_stacksize':2,\n 'cam_awbg_blue':0,\n 'cam_awbg_red':0,\n 'cam_contrast':1,\n 'cam_cropx':0,\n 'cam_cropy':0,\n 'cam_delay_after':0,\n 'cam_delay_before':0,\n 'camera':'',\n 'cam_gain':1,\n 'cam_jpeg_quality':95,\n 'cam_rotation':0,\n 'cam_saturation':1,\n 'cam_shutter':50000,\n 'cam_timeout':800,\n 'hostname':'openscan',\n 'model':'',\n 'osc_credit':'',\n 'osc_limit_filesize':'',\n 'osc_limit_photos':'',\n 'osc_splitsize':200000000,\n 'pin_endstop1':24,\n 'pin_endstop2':25,\n 'pin_external':10,\n 'pin_ringlight1':17,\n 'pin_ringlight2':27,\n 'pin_rotor_dir':5,\n 'pin_rotor_enable':23,\n 'pin_rotor_step':6,\n 'pin_tt_dir':9,\n 'pin_tt_enable':22,\n 'pin_tt_step':11,\n 'rotor_acc':1,\n 'rotor_accramp':2000,\n 'rotor_angle':10,\n 'rotor_anglemax':60,\n 'rotor_anglemin':-20,\n 'rotor_anglestart':0,\n 'rotor_delay':0.0001,\n 'rotor_dir':1,\n 'rotor_stepsperrotation':48000,\n 'routine_photocount':50,\n 'routine_projectname':'default',\n 'smb':true,\n 'ssh':true,\n 'status_cloud':'ready',\n 'status_internal_cam':'--READY--',\n 'status_uploadprogress':'',\n 'terms':false,\n 'token':'',\n 'tt_acc':1,\n 'tt_accramp':200,\n 'tt_angle':10,\n 'tt_delay':0.0001,\n 'tt_dir':1,\n 'tt_stepsperrotation':3200,\n 'cam_focus':2838,\n 'cam_focus1':0,\n 'cam_focus2':0,\n 'uploadprogress':'',\n 'update_type':'beta',\n 'update_auto':true,\n 'downscale_threshold':1000,\n 'turntable_mode':false,\n 'timeout_ringlight':300,\n 'diskspace_threshold':4000,\n 'updateable':false,\n 'cam_focuspeak':false,\n 'cam_histogram':false,\n 'routine_secondpass':true,\n 'cam_output_resolution':20000000,\n 'cam_preview_resolution':2000000,\n 'cam_output_downscale':false,\n}}\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 310, "y": 800, "wires": [ [ "c77552216a8bb781" ] ] }, { "id": "a1f0ed7d5a9d670e", "type": "inject", "z": "829d803b6033a693", "name": "", "props": [ { "p": "overwrite", "v": "false", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "0.1", "topic": "", "x": 90, "y": 800, "wires": [ [ "544d20f02215011a" ] ] }, { "id": "c77552216a8bb781", "type": "python3-function", "z": "829d803b6033a693", "name": "chk files", "func": "from os import listdir\n\nbasedir = '/home/pi/OpenScan/settings/'\n\nmsg['payload'] = ''\n\nfiles = listdir(basedir)\n\nfor i in msg['settings']:\n if msg['overwrite'] != True:\n if i not in files:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n else:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n\nmsg['payload'] = True\n\nreturn msg", "outputs": 1, "x": 520, "y": 800, "wires": [ [ "960912e90ba5b5bc" ] ] }, { "id": "38783aea9cc317a6", "type": "link in", "z": "829d803b6033a693", "name": "factory reset", "links": [ "80bccc884b0be297", "beacc3dc5398fa79" ], "x": 135, "y": 840, "wires": [ [ "544d20f02215011a" ] ] }, { "id": "f20da2fc4978b7bf", "type": "link out", "z": "829d803b6033a693", "name": "global", "mode": "link", "links": [ "d14bbbb446d45e39" ], "x": 345, "y": 400, "wires": [] }, { "id": "8e39e4a037487ecd", "type": "python3-function", "z": "829d803b6033a693", "name": "create log", "func": "import subprocess\nfrom time import sleep\nsleep(20)\n\n\nlog = '############################################DMESG############################################\\n'\nlog += subprocess.getoutput(\"dmesg\")\nlog += '\\n############################################SYSLOG############################################\\n'\nlog += subprocess.getoutput(\"tail -10000 /var/log/syslog\")\n\nwith open('/home/pi/OpenScan/tmp/log.txt', 'w+') as file:\n file.write(log)\n\nreturn msg", "outputs": 1, "x": 240, "y": 560, "wires": [ [] ] }, { "id": "be8cae9cf6f3585f", "type": "ui_template", "z": "829d803b6033a693", "group": "1f7f7e1e24f5ad9b", "name": "first start", "order": 1, "width": 6, "height": 3, "format": "

Initial Setup

\n

Note, that you can always adjust these and other settings in the settings menu, which will appear after this setup stage. 

\n

Model

\n

Please select the OpenScan Version - this will only affect the motor settings (acceleration, gear ratio, speed).

\n

Camera

\n

- Pi Camera v1, v2, HQ, Arducam IMX519, IMX290, IMX378, OV9281 are connected through the ribbon cable. If you encounter any issues, please check the cable's orientation

\n

- DSLR (gphoto) - can be used with a wide range of cameras, which can be connected and controlled via USB. Check GPhoto if your camera is supported

\n

- External Camera - Can be used to connect your camera trigger to the GPIO pins on the front of the pi shield. This can be used with any (modified) remote shutter release, and thus it is possible to use Smartphones, DSLR and compact cameras

", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 280, "y": 40, "wires": [ [] ] }, { "id": "8955d11554f55e63", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "5b3e5aca21140e9a", "order": 1, "width": 6, "height": 3, "passthru": false, "label": "Install Updates", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "date", "topic": "", "topicType": "str", "x": 120, "y": 280, "wires": [ [ "1e7457ea9c2c5e09" ] ] }, { "id": "1e7457ea9c2c5e09", "type": "link out", "z": "829d803b6033a693", "name": "update", "mode": "link", "links": [ "39a502b38837273d" ], "x": 245, "y": 280, "wires": [] }, { "id": "bb84b9e5c7d8e21f", "type": "python3-function", "z": "829d803b6033a693", "name": "rescue incomplete project", "func": "#if project has not been done properly, this is a way to rescue the file\n\nfrom os import system\nfrom os.path import isfile\nfrom time import strftime\nfrom OpenScan import load_str\n\nbasepath = '/home/pi/OpenScan/'\nzippath = basepath + 'tmp/tmp.zip'\nprojectname=load_str(\"routine_projectname\")\nprojectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\n\nif isfile(zippath):\n system('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')", "outputs": 1, "x": 290, "y": 600, "wires": [ [] ] }, { "id": "a291fc98e4269c1b", "type": "ui_text", "z": "829d803b6033a693", "group": "729f9ea6e3513c9b", "order": 7, "width": 4, "height": 1, "name": "version", "label": "Version:", "format": "{{msg.firmware}}", "layout": "row-center", "className": "", "x": 460, "y": 360, "wires": [] }, { "id": "7113d7b25a851151", "type": "function", "z": "829d803b6033a693", "name": "FIRMWARE VERSION", "func": "msg.firmware = '2024-11-11'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 360, "wires": [ [ "a291fc98e4269c1b", "ec5cefa70ff535f7" ] ] }, { "id": "ec5cefa70ff535f7", "type": "ui_text", "z": "829d803b6033a693", "group": "ddbd496e.93a288", "order": 2, "width": 6, "height": 1, "name": "current version", "label": "Current version:", "format": "{{msg.firmware}}", "layout": "row-spread", "className": "", "x": 480, "y": 320, "wires": [] }, { "id": "c4c1580c289fc7bd", "type": "python3-function", "z": "829d803b6033a693", "name": "create path", "func": "import os\n\npaths = ['/home/pi/OpenScan/scans/preview/']\n\n\nfor i in paths:\n if not os.path.isdir(i):\n os.mkdir(i)", "outputs": 1, "x": 250, "y": 640, "wires": [ [] ] }, { "id": "c5e5e75ed45efbaa", "type": "ui_template", "z": "829d803b6033a693", "group": "7aaf184330605300", "name": "donate", "order": 2, "width": "0", "height": "0", "format": "\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "global", "className": "", "x": 430, "y": 40, "wires": [ [] ] }, { "id": "828e5298.d2192", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 9, "width": 2, "height": 1, "passthru": false, "label": "⇐", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 400, "wires": [ [ "b12e54fb.3141b8" ] ] }, { "id": "96c7e241.458e6", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 10, "width": 2, "height": 1, "passthru": false, "label": "⇒", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 440, "wires": [ [ "37f52dd4.bd7572" ] ] }, { "id": "2e854876.6b6008", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 6, "width": 2, "height": 1, "passthru": true, "label": "⇑", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 280, "wires": [ [ "555aea34.b3b5e4" ] ] }, { "id": "753817f.1b9b3e8", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 7, "width": 2, "height": 1, "passthru": true, "label": "⇓", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 320, "wires": [ [ "9905e0c9.dddcd" ] ] }, { "id": "8775044.3aa3ef8", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 8, "width": 2, "height": 1, "name": "", "label": "Turntable", "format": "", "layout": "row-left", "className": "", "x": 100, "y": 360, "wires": [] }, { "id": "9e8a2d23.bf6ce", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 5, "width": 2, "height": 1, "name": "", "label": "Rotor", "format": "", "layout": "row-left", "className": "", "x": 90, "y": 240, "wires": [] }, { "id": "555aea34.b3b5e4", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 280, "wires": [ [ "46e00b45.c24ca4" ] ] }, { "id": "9905e0c9.dddcd", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 320, "wires": [ [ "6ee089cb343a35ef" ] ] }, { "id": "b12e54fb.3141b8", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 400, "wires": [ [ "c1871a2b9af5419a" ] ] }, { "id": "37f52dd4.bd7572", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 440, "wires": [ [ "42b9f1fc49e69f54" ] ] }, { "id": "46e00b45.c24ca4", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Rotor left", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('rotor',load_int('rotor_angle'))", "outputs": 1, "x": 360, "y": 280, "wires": [ [] ] }, { "id": "6ee089cb343a35ef", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Rotor right", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('rotor',-load_int('rotor_angle'))", "outputs": 1, "x": 370, "y": 320, "wires": [ [] ] }, { "id": "42b9f1fc49e69f54", "type": "python3-function", "z": "1613373abaf77a2c", "name": "TT right", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('tt',-load_int('tt_angle'))", "outputs": 1, "x": 360, "y": 440, "wires": [ [] ] }, { "id": "c1871a2b9af5419a", "type": "python3-function", "z": "1613373abaf77a2c", "name": "TT left", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('tt',load_int('tt_angle'))", "outputs": 1, "x": 350, "y": 400, "wires": [ [] ] }, { "id": "107a030938cbfea9", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 540, "wires": [ [ "cb6ebdabaaf7d0da" ] ] }, { "id": "ce28a0b5bfb0d5a1", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 540, "wires": [ [] ] }, { "id": "84d6b96c8ebaac96", "type": "function", "z": "1613373abaf77a2c", "name": "loadF", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) / 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 580, "wires": [ [ "c8a3fde5206ce1ae" ] ] }, { "id": "44c3947a9b92d32d", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload * 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 580, "wires": [ [] ] }, { "id": "9c6b48b7b4cc4e1a", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 620, "wires": [ [ "9daea4bd57f7a00e" ] ] }, { "id": "c470fd0b15356206", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 660, "wires": [ [ "87be854db758a9a6" ] ] }, { "id": "c2b2ab5524271123", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 620, "wires": [ [] ] }, { "id": "26f17a7f406df73c", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 660, "wires": [ [] ] }, { "id": "ebbf11b55d758806", "type": "ui_text_input", "z": "1613373abaf77a2c", "name": "", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 4, "width": 3, "height": 1, "passthru": true, "mode": "text", "delay": "0", "topic": "", "sendOnBlur": true, "className": "", "topicType": "str", "x": 320, "y": 500, "wires": [ [ "67385b196c517ac6" ] ] }, { "id": "f4b3112a9ec6c487", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.payload=\"default\"\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 500, "wires": [ [ "ebbf11b55d758806" ] ] }, { "id": "67385b196c517ac6", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'routine_projectname'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload).replace(/ /g, '_')\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 500, "wires": [ [] ] }, { "id": "4dd7285c2b0fd79b", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "ringlight", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 12, "width": 3, "height": 1, "passthru": true, "outs": "all", "topic": "", "topicType": "str", "min": 0, "max": "3", "step": 1, "className": "", "x": 320, "y": 700, "wires": [ [ "873dace18a23fdf2" ] ] }, { "id": "873dace18a23fdf2", "type": "python3-function", "z": "1613373abaf77a2c", "name": "LED", "func": "from OpenScan import ringlight\nval = msg['payload']\n\nif val == 0:\n ringlight(1,False)\n ringlight(2,False)\nelif val == 1:\n ringlight(1,False)\n ringlight(2,True)\nelif val == 2:\n ringlight(1,True)\n ringlight(2,False)\nelif val == 3:\n ringlight(1,True)\n ringlight(2,True)", "outputs": 1, "x": 510, "y": 700, "wires": [ [] ] }, { "id": "9e30e33a1520fee0", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "msg.payload = 0\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 700, "wires": [ [ "4dd7285c2b0fd79b" ] ] }, { "id": "7dd287f40385922f", "type": "ui_button", "z": "1613373abaf77a2c", "name": "start ", "group": "7aaf184330605300", "order": 17, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-play", "payload": "", "payloadType": "date", "topic": "enabled", "topicType": "str", "x": 150, "y": 880, "wires": [ [ "431f917c2541ae48", "33d94a04b96a2de0", "6d15f717d5a11002" ] ] }, { "id": "579f2211199fd6ab", "type": "ui_button", "z": "1613373abaf77a2c", "name": "stop", "group": "7aaf184330605300", "order": 19, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-stop", "payload": "numberofphotos", "payloadType": "global", "topic": "", "topicType": "str", "x": 810, "y": 960, "wires": [ [ "1787f08ed7070ddd", "c1c044f3c2139f68" ] ] }, { "id": "431f917c2541ae48", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Routine", "func": "from OpenScan import load_bool, load_str, load_int, load_float, motorrun, create_coordinates, take_photo, save, load_bool, camera\nfrom time import sleep, strftime, time\nfrom zipfile import ZipFile, ZIP_DEFLATED\nfrom os import system\nfrom os.path import isfile\nfrom Arducam import Focuser\n\nif load_str(\"status_internal_cam\")==\"no camera found\" or load_str(\"status_internal_cam\")[:5]==\"Featu\":\n return\n\nsave('status_internal_cam','Routine-preparing')\n\nprojectname=load_str(\"routine_projectname\")\nphotocount = load_int('routine_photocount') #vorher point_count\nangle_max = load_int('rotor_anglemax')\nangle_min = load_int('rotor_anglemin')\nangle_start = load_int('rotor_anglestart')\ncam = load_str('camera')\nSTmode = load_bool('cam_STmode')\ntt_mode = load_bool('turntable_mode')\ncam_delay_after = load_float('cam_delay_after')\ncam_delay_before = load_float('cam_delay_before')\n\nif cam == 'imx519' and STmode == True:\n focuser = Focuser('/dev/v4l-subdev1')\n stacksize = load_int('cam_stacksize')\n focus1 = load_int('cam_focus1')\n focus2 = load_int('cam_focus2')\n if focus1 > focus2:\n focus2 = focus1\n focus1 = load_int('cam_focus2') \n focusstep = int((focus2-focus1)/(stacksize - 1))\n\ncounter = 0\nbasepath = '/home/pi/OpenScan/'\ntemppath = basepath + 'tmp/tmp.jpg'\nzippath = basepath + 'tmp/tmp.zip'\n\nif not 'projectcode' in msg:\n projectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\n msg['projectcode'] = projectcode\n msg['counter'] = -1\n if isfile(zippath):\n system('rm ' + zippath)\n sleep(1)\n\nprojectcode = msg['projectcode']\nmsg['counter'] += 1\n\nif tt_mode == False:\n coordinates = create_coordinates(angle_min,angle_max,photocount)\nelse:\n angle_start = 0\n coordinates = []\n for i in range (photocount):\n coordinates.append([0,360/photocount*(i+1)])\n\nposition_last = (angle_start , 0)\n\nzip = ZipFile(zippath, \"a\",ZIP_DEFLATED, allowZip64=True)\n\nstarttime = time()\n\nfor position in coordinates:\n counter += 1\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n\n while load_str('status_internal_cam') == 'Routine-paused':\n sleep(0.2)\n\n filepath = basepath + 'tmp/' + projectname + '_' + str(counter) + \".jpg\"\n\n rotor_angle = position_last[0] - position[0]\n if abs(rotor_angle) > 180:\n rotor_angle = -360 * rotor_angle/abs(rotor_angle) + rotor_angle\n\n tt_angle = position_last[1] - position[1]\n if tt_angle > 180:\n tt_angle -= 360\n elif tt_angle < -180:\n tt_angle += 360\n # tt_angle = -360 * tt_angle/abs(tt_angle) + tt_angle\n \n motorrun('rotor', rotor_angle)\n motorrun('tt', tt_angle)\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n while load_str('status_internal_cam') == 'Routine-paused':\n sleep(0.2)\n\n msg['cropx'] = load_int('cam_cropx')\n msg['cropy'] = load_int('cam_cropy')\n msg['rotation'] = load_int('cam_rotation')\n msg['filepath_in'] = 'tmp/tmp.jpg'\n msg['filepath_out'] = 'tmp/tmp.jpg'\n msg['filepath'] = 'tmp/tmp.jpg'\n\n if counter < 6:\n ETA = ''\n sleep(cam_delay_before)\n if STmode == True:\n counter2 = 0\n for focus in range (stacksize):\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n while load_str('status_internal_cam') == 'Routine-paused':\n sleep(0.2)\n counter2 += 1\n save('status_internal_cam','Routine-' + str(counter) + '/' + str(photocount) + ' F' + str(counter2) + ETA)\n focuser.write(focus1 + focus * focusstep)\n take_photo('tmp/tmp.jpg')\n camera('/crop',msg)\n zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + '-' + str(focus) + \".jpg\")\n system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\n elif cam != 'external':\n save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\n\n if cam == 'gphoto':\n camera('/gphoto_capture', msg)\n if cam in ('usb_webcam','imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\n take_photo('tmp/tmp.jpg')\n camera('/crop',msg)\n \n zip.write(temppath, projectname + '_' + str(msg['counter']) + '_' + str(counter) + \".jpg\")\n system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\n elif cam == 'external':\n camera('external_capture')\n save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount) + ETA)\n\n ETA = '-ETA:'+str(int((photocount/counter - 1)*(time() - starttime)))+'/'+str(int(photocount/counter*(time() - starttime)))+'s'\n sleep(cam_delay_after)\n\n position_last = position\n\nzip.close()\n\nsave('status_internal_cam','Routine-done')\n\nmotorrun('rotor',position_last[0] - angle_start)\nmotorrun('tt',position_last[1])\n\nsave('status_internal_cam','--READY--')\n\nif load_bool('routine_secondpass')==True:\n msg['topic'] = 'Scan done'\n msg['payload'] = 'Do you want to run another pass or finish this project?'\n msg['enabled'] = False\n return msg,None\n\nreturn None,msg\n", "outputs": 2, "x": 300, "y": 880, "wires": [ [ "db7eea74d3bf892b" ], [ "0b8661103366f834" ] ] }, { "id": "1787f08ed7070ddd", "type": "python3-function", "z": "1613373abaf77a2c", "name": "stop", "func": "from OpenScan import load_str, save\n\nstatus = load_str('status_internal_cam')\n\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\n return\n\nsave('status_internal_cam', 'Routine-stopping')", "outputs": 1, "x": 930, "y": 960, "wires": [ [] ] }, { "id": "e9b13dfd9f8d3711", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6" ], "x": 395, "y": 840, "wires": [] }, { "id": "9654deebb668e012", "type": "inject", "z": "1613373abaf77a2c", "name": "1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "", "payloadType": "date", "x": 290, "y": 1000, "wires": [ [ "c1c044f3c2139f68" ] ] }, { "id": "8367cfa0bf5bc5df", "type": "link in", "z": "1613373abaf77a2c", "name": "start routine", "links": [ "210ef5246d1a8790", "84608db962fd9932", "8689e938.dd9e38", "f20f2dbc.0f123", "e9b13dfd9f8d3711", "96bdb9417e38810f", "fb13752beddee9f2", "bd75f33b8a57c522" ], "x": 55, "y": 880, "wires": [ [ "7dd287f40385922f" ] ] }, { "id": "fb13752beddee9f2", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "2f4c0f98.dee2", "8367cfa0bf5bc5df", "b33d604c.5f1a6" ], "x": 895, "y": 920, "wires": [] }, { "id": "95439678bb2df2a2", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "msg.flag = global.get('flag')\nif (global.get('flag_pw')== true){\n return msg\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 1220, "wires": [ [ "04cc2467807d2d6b", "14f9617b5b301318" ] ] }, { "id": "948a3ae4444685f2", "type": "change", "z": "1613373abaf77a2c", "name": "flag_pw true", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 610, "y": 1260, "wires": [ [] ] }, { "id": "04cc2467807d2d6b", "type": "change", "z": "1613373abaf77a2c", "name": "flag_pw false", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 390, "y": 1260, "wires": [ [] ] }, { "id": "12f1399b240830bf", "type": "exec", "z": "1613373abaf77a2c", "command": " v4l2-ctl --list-formats-ext", "addpay": "", "append": "", "useSpawn": "true", "timer": "", "winHide": false, "oldrc": false, "name": "check cam", "x": 190, "y": 100, "wires": [ [ "6222f781629c72e7" ], [ "6222f781629c72e7" ], [] ] }, { "id": "6222f781629c72e7", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\ncontent = '--READY--'\n\nif (msg.payload.includes('Cannot open device')){\n content = 'no camera found'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return msg\n }\n });\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 350, "y": 100, "wires": [ [ "e89c16809f8a5f1c" ] ] }, { "id": "e978bf8c53d1f15a", "type": "comment", "z": "1613373abaf77a2c", "name": "Settings internal cam", "info": "", "x": 120, "y": 40, "wires": [] }, { "id": "ccb7da246de908d1", "type": "comment", "z": "1613373abaf77a2c", "name": "preview internal cam", "info": "", "x": 110, "y": 1160, "wires": [] }, { "id": "e9566588c5e40637", "type": "inject", "z": "1613373abaf77a2c", "name": "4s/0.5", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.5", "crontab": "", "once": true, "onceDelay": "4", "topic": "Repeat", "payload": "0.2", "payloadType": "str", "x": 80, "y": 1220, "wires": [ [ "95439678bb2df2a2" ] ] }, { "id": "14f9617b5b301318", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Take Preview Shot", "func": "from time import time, sleep\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\nsleep(0.1)\n\n\nstatus = load_str('status_internal_cam')\ncam=load_str('camera')\n\n\nif msg['flag'] == False and not 'Routine' in status:\n return msg\n\nif cam == 'external':\n return\n\nmsg['payload']=\"/tmp/preview.jpg?ts=\"+str(int(time()*10))\n\nif cam == 'gphoto' and status == 'no camera found':\n if camera('/gphoto_init') == 200:\n save('status_internal_cam','--READY--')\n\nif status!=\"--READY--\":\n return msg\n\nmsg['cropx'] = load_int('cam_cropx')\nmsg['cropy'] = load_int('cam_cropy')\nmsg['rotation'] = load_int('cam_rotation')\nmsg['filepath_in'] = 'tmp/tmp.jpg'\nmsg['filepath_out'] = 'tmp/preview.jpg'\nmsg['filepath'] = 'tmp/tmp.jpg'\nmsg['preview'] = True\n\nif cam == 'gphoto':\n if camera('/gphoto_test', msg) != 200:\n save('status_internal_cam','no camera found')\n return msg\n camera('/gphoto_preview', msg)\n\nif cam in ('usb_webcam', 'imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','imx519'):\n take_photo('tmp/tmp.jpg')\n\ncamera('/crop',msg)\n\nreturn msg\n", "outputs": 1, "x": 430, "y": 1220, "wires": [ [ "948a3ae4444685f2", "991b587d406d0d91", "8f5d87ce24c40b11" ] ] }, { "id": "991b587d406d0d91", "type": "ui_template", "z": "1613373abaf77a2c", "group": "ce9cc9d915dc6eb6", "name": "preview_internal", "order": 1, "width": 12, "height": 12, "format": "
\n\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 620, "y": 1220, "wires": [ [] ] }, { "id": "1118d0965ff7c40b", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 3, "width": 3, "height": 1, "name": "projectname", "label": "Projectname", "format": "", "layout": "row-left", "className": "", "x": 670, "y": 500, "wires": [] }, { "id": "82c8ad50ecfbc755", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 11, "width": 3, "height": 1, "name": "ringlight", "label": "Ringlight", "format": "", "layout": "row-left", "className": "", "x": 660, "y": 700, "wires": [] }, { "id": "33d94a04b96a2de0", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "global.set('flag', false)\n\nvar file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\n\n\nif (data === 'no camera found' || data.substring(0,5) === 'Featu'){\n return\n}\n\nmsg.enabled = true\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 960, "wires": [ [ "579f2211199fd6ab", "c433515042ba01b5" ] ] }, { "id": "c1c044f3c2139f68", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 1000, "wires": [ [ "579f2211199fd6ab", "c433515042ba01b5" ] ] }, { "id": "9a368472a72fbc48", "type": "comment", "z": "1613373abaf77a2c", "name": "preview arducam with focus", "info": "", "x": 140, "y": 1360, "wires": [] }, { "id": "8f5d87ce24c40b11", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "preview_arducam", "order": 2, "width": 10, "height": 12, "format": "
\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 630, "y": 1300, "wires": [ [] ] }, { "id": "282efe64332193c8", "type": "python3-function", "z": "1613373abaf77a2c", "name": "focus", "func": "from OpenScan import load_str\n\nif load_str('camera') != 'imx519':\n return\n\nfrom Arducam import Focuser\n\n\nif msg['focuser'] == True:\n focuser = Focuser('/dev/v4l-subdev1')\n focuser.write(msg['focus'])\n return msg", "outputs": 1, "x": 1110, "y": 1460, "wires": [ [] ] }, { "id": "64b16ef47ab6d859", "type": "ui_switch", "z": "1613373abaf77a2c", "name": "MF", "label": "", "tooltip": "", "group": "90223f7ddc082321", "order": 4, "width": 1, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "false", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "true", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 150, "y": 1400, "wires": [ [ "f017f67a8d4a3750" ] ] }, { "id": "f017f67a8d4a3750", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "let fs = global.get('fs');\nfilepath = '/home/pi/OpenScan/settings/';\n\nvar file = 'status_internal_cam'\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data != '--READY--'){\n return\n}\n\nfile = 'cam_AFmode'\ncontent = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n});\n\nglobal.set('AF',msg.payload)\nmsg.enabled = false\nif (msg.payload == false){\n msg.enabled = true\n}\nif (msg.payload == true){\n file = 'cam_focus1'\n content = String(0)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n file = 'cam_focus2'\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \n file = 'cam_stacksize'\n content = String(2)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n global.set('focus1', 0)\n global.set('focus2', 0)\n\n}\n\n\nmsg.focus = global.get('focus')\nmsg.payload = 'down'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 1400, "wires": [ [ "5c39bd09.702d84", "74521cf72050b515", "b70e8c24ee011258", "a2ff9dfd858821bc", "ef62086d10d830fd" ] ] }, { "id": "65145c939b6647e2", "type": "link in", "z": "1613373abaf77a2c", "name": "", "links": [ "960912e90ba5b5bc" ], "x": 55, "y": 1400, "wires": [ [ "64b16ef47ab6d859" ] ] }, { "id": "5ea18678.975138", "type": "trigger", "z": "1613373abaf77a2c", "name": "20ms", "op1": "", "op2": "0", "op1type": "pay", "op2type": "str", "duration": "-20", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 1440, "wires": [ [ "fd93843e238cc9ce" ] ] }, { "id": "5c39bd09.702d84", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "F+", "order": 8, "width": 1, "height": 1, "format": " ", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 410, "y": 1400, "wires": [ [ "dcfb5cce.0431a" ] ] }, { "id": "dcfb5cce.0431a", "type": "switch", "z": "1613373abaf77a2c", "name": "", "property": "payload", "propertyType": "msg", "rules": [ { "t": "eq", "v": "1", "vt": "num" }, { "t": "eq", "v": "-1", "vt": "num" }, { "t": "eq", "v": "up", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 3, "x": 550, "y": 1420, "wires": [ [ "5ea18678.975138", "f4a41b1e7b221486" ], [ "5ea18678.975138", "f4a41b1e7b221486" ], [ "8cdd0a6b.40bcd8" ] ] }, { "id": "8cdd0a6b.40bcd8", "type": "change", "z": "1613373abaf77a2c", "name": "", "rules": [ { "t": "set", "p": "reset", "pt": "msg", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 560, "y": 1480, "wires": [ [ "5ea18678.975138", "e9b3837b1ffb0360" ] ] }, { "id": "74521cf72050b515", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "F-", "order": 9, "width": 1, "height": 1, "format": " ", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 410, "y": 1440, "wires": [ [ "dcfb5cce.0431a" ] ] }, { "id": "7219f62c9fdc6753", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 7, "width": 2, "height": 1, "name": "", "label": "{{msg.payload}}", "format": "", "layout": "col-center", "className": "", "x": 1130, "y": 1420, "wires": [] }, { "id": "b70e8c24ee011258", "type": "function", "z": "1613373abaf77a2c", "name": "global", "func": "if (msg.payload == 'down'){\n msg.enabled = false\n msg.payload = ' '\n msg.focuser = global.get('focuser')\n return msg\n}\n\n\nmsg.enabled = true\n\nsign = msg.payload\nfocus = global.get('focus')\nif (focus > 3000){\n focusstep = 5\n}\nelse if (focus <=3000 && focus > 2000){\n focusstep = 3\n}\nelse{\n focusstep = 2\n}\n\n\nfocus = focus + sign * focusstep\n\nsign = msg.payload\nif (focus > 4000){\n distance = 6\n focus = 4000\n}\nelse if (focus > 1200 && focus <= 4000){\n distance = 737086 * Math.pow(focus, -1.4096)\n}\nelse if (focus <= 1200){\n distance = 999\n if (focus <=0){\n focus = 0\n }\n}\n\n\nglobal.set('focus', focus)\nmsg.focus = focus\nmsg.distance = distance\ndistance = distance * 10\nmsg.focuser = global.get('focuser')\nmsg.payload = String(distance.toFixed(1)) + 'mm'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 970, "y": 1440, "wires": [ [ "7219f62c9fdc6753", "282efe64332193c8", "704a9f89089d1f25" ] ] }, { "id": "f4a41b1e7b221486", "type": "change", "z": "1613373abaf77a2c", "name": "focuser f", "rules": [ { "t": "set", "p": "focuser", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 740, "y": 1400, "wires": [ [] ] }, { "id": "e9b3837b1ffb0360", "type": "change", "z": "1613373abaf77a2c", "name": "focuser t", "rules": [ { "t": "set", "p": "focuser", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 740, "y": 1480, "wires": [ [] ] }, { "id": "fd93843e238cc9ce", "type": "delay", "z": "1613373abaf77a2c", "name": "10ms", "pauseType": "delay", "timeout": "20", "timeoutUnits": "milliseconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 850, "y": 1440, "wires": [ [ "b70e8c24ee011258" ] ] }, { "id": "25c4138bddb77b6b", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "set", "order": 10, "width": 2, "height": 1, "format": "set", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 570, "y": 1540, "wires": [ [ "95e1d239988b29e0" ] ] }, { "id": "95e1d239988b29e0", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "focus = global.get('focus')\nfocus1 = global.get('focus1')\nfocus2 = global.get('focus2')\nlet fs = global.get('fs');\nfilepath = '/home/pi/OpenScan/settings/';\n \nif (msg.payload == false){\n return msg\n}\n\nif (focus1 != 0 && focus2 != 0){\n global.set('focus1', 0)\n global.set('focus2', 0)\n file = 'cam_focus1'\n content = String(0)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n file = 'cam_focus2'\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n msg.distance1 = ' '\n msg.distance2 = ' '\n msg.enabled = false\n return msg\n}\n\nif (focus > 4000){\n distance = 6\n focus = 4000\n}\nelse if (focus > 1200 && focus <= 4000){\n distance = 737086 * Math.pow(focus, -1.4096)\n}\nelse if (focus <= 1200){\n distance = 999.9\n if (focus <=0){\n focus = 0\n }\n}\ndistance = distance * 10\n\nif (focus1 == 0){\n global.set('focus1', focus)\n file = 'cam_focus1'\n content = String(focus)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n global.set('distance1', distance)\n msg.distance1 = distance.toFixed(1)\n msg.distance2 = 'tbd'\n msg.enabled = false\n return msg\n}\nif (focus1 != 0 && focus2 ==0 && focus!= focus1){\n global.set('focus2', focus)\n file = 'cam_focus2'\n content = String(focus)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n global.set('distance2', distance)\n msg.distance1 = global.get('distance1').toFixed(1)\n msg.distance2 = distance.toFixed(1)\n msg.enabled = true\n return msg\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 710, "y": 1560, "wires": [ [ "7889245e91ddea4b", "210ef5246d1a8790" ] ] }, { "id": "7889245e91ddea4b", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 11, "width": 2, "height": 1, "name": "", "label": "{{msg.distance1}}", "format": "{{msg.distance2}}", "layout": "col-center", "className": "", "x": 830, "y": 1600, "wires": [] }, { "id": "a1d29e56599da0bd", "type": "link in", "z": "1613373abaf77a2c", "name": "focusnumber", "links": [ "210ef5246d1a8790", "2dd2503d7ab0214b", "6b94bf2295b1b31d" ], "x": 175, "y": 1760, "wires": [ [ "06504f47ee1744d7", "5f8b90ef08a7d68c" ] ] }, { "id": "210ef5246d1a8790", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "a1d29e56599da0bd", "8367cfa0bf5bc5df", "149e2e46b9623a2d" ], "x": 835, "y": 1560, "wires": [] }, { "id": "b6f37e23f2491639", "type": "ui_switch", "z": "1613373abaf77a2c", "name": "Stack", "label": "", "tooltip": "", "group": "90223f7ddc082321", "order": 6, "width": 1, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 290, "y": 1600, "wires": [ [ "2d66216fee29250c" ] ] }, { "id": "a2ff9dfd858821bc", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "msg.payload = false\nif (msg.enabled == false){\n return msg\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 1560, "wires": [ [ "25c4138bddb77b6b", "7889245e91ddea4b", "4cfada2de1c5bb74", "95e1d239988b29e0" ] ] }, { "id": "2d66216fee29250c", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "file = 'cam_STmode'\nlet fs = global.get('fs');\nfilepath = '/home/pi/OpenScan/settings/';\ncontent = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n});\n\nmsg.enabled = true\nglobal.set('ST',msg.payload)\nif (msg.payload == false){\n global.set('focus1',0)\n global.set('focus2',0)\n file = 'cam_focus1'\n content = String(0)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n file = 'cam_focus2'\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \n \n msg.enabled = false\n}\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 430, "y": 1600, "wires": [ [ "25c4138bddb77b6b", "7889245e91ddea4b", "2dd2503d7ab0214b", "4cfada2de1c5bb74" ] ] }, { "id": "ef62086d10d830fd", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "msg.payload = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 150, "y": 1560, "wires": [ [ "b6f37e23f2491639", "523019d0a2c698f5" ] ] }, { "id": "06504f47ee1744d7", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 12, "width": 2, "height": 1, "name": "", "label": "Stacksize:", "format": "{{msg.stacksize}}", "layout": "row-center", "className": "", "x": 710, "y": 1760, "wires": [] }, { "id": "2dd2503d7ab0214b", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "a1d29e56599da0bd" ], "x": 535, "y": 1620, "wires": [] }, { "id": "21306d6402225553", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.stacksize = msg.payload\nmsg.enabled = true\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 550, "y": 1720, "wires": [ [ "06504f47ee1744d7", "ca184d58f7deb4b1", "84608db962fd9932" ] ] }, { "id": "e2f8fdd47bdd1b66", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "stacksize", "label": " ", "tooltip": "", "group": "90223f7ddc082321", "order": 13, "width": 2, "height": 1, "passthru": true, "outs": "end", "topic": "", "topicType": "str", "min": "2", "max": "20", "step": "1", "className": "", "x": 400, "y": 1720, "wires": [ [ "21306d6402225553" ] ] }, { "id": "523019d0a2c698f5", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 5, "width": 1, "height": 1, "name": "", "label": "St", "format": "", "layout": "col-center", "className": "", "x": 290, "y": 1560, "wires": [] }, { "id": "dfbfe28bac5c4221", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 3, "width": 1, "height": 1, "name": "MF", "label": "MF", "format": "", "layout": "col-center", "className": "", "x": 150, "y": 1440, "wires": [] }, { "id": "ca184d58f7deb4b1", "type": "function", "z": "1613373abaf77a2c", "name": "save", "func": "var file = 'cam_stacksize'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.stacksize)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 690, "y": 1720, "wires": [ [] ] }, { "id": "704a9f89089d1f25", "type": "function", "z": "1613373abaf77a2c", "name": "save", "func": "var file = 'cam_focus'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.focus)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1110, "y": 1500, "wires": [ [] ] }, { "id": "5f8b90ef08a7d68c", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_stacksize'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 1720, "wires": [ [ "e2f8fdd47bdd1b66" ] ] }, { "id": "4cfada2de1c5bb74", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "if (msg.enabled == true){\n msg.enabled = false\n}\nelse{\n msg.enabled = true\n}\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 570, "y": 1660, "wires": [ [ "84608db962fd9932" ] ] }, { "id": "84608db962fd9932", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "149e2e46b9623a2d" ], "x": 675, "y": 1660, "wires": [] }, { "id": "e89c16809f8a5f1c", "type": "python3-function", "z": "1613373abaf77a2c", "name": "gphoto", "func": "\nfrom OpenScan import camera, save, load_str\n\nif load_str('camera') == 'gphoto':\n if camera('/gphoto_init') == 200:\n if camera('/gphoto_test') == 200:\n save('status_internal_cam','--READY--')\n return msg\nif load_str('camera') == 'external':\n save('status_internal_cam','--READY--')", "outputs": 1, "x": 470, "y": 100, "wires": [ [] ] }, { "id": "917a194be245384a", "type": "link in", "z": "1613373abaf77a2c", "name": "enable projectname", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 540, "wires": [ [ "f4b3112a9ec6c487" ] ] }, { "id": "65cef204b16f8741", "type": "link in", "z": "1613373abaf77a2c", "name": "enable shutter", "links": [ "2d76e5617f13cd6c", "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 580, "wires": [ [ "84d6b96c8ebaac96" ] ] }, { "id": "2aea1727dbea76ce", "type": "link in", "z": "1613373abaf77a2c", "name": "enable cropx", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 620, "wires": [ [ "9c6b48b7b4cc4e1a" ] ] }, { "id": "4f212b44aa487945", "type": "link in", "z": "1613373abaf77a2c", "name": "enable cropy", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 660, "wires": [ [ "c470fd0b15356206" ] ] }, { "id": "6d1e12f51f9af0b6", "type": "link in", "z": "1613373abaf77a2c", "name": "start camchk", "links": [ "960912e90ba5b5bc" ], "x": 55, "y": 100, "wires": [ [ "12f1399b240830bf" ] ] }, { "id": "8ebd1dcb5db156ed", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 2, "width": 6, "height": 1, "name": "", "label": "Current Status:", "format": " {{msg.payload}} ", "layout": "row-spread", "className": "", "x": 320, "y": 160, "wires": [] }, { "id": "94a7aec739f9266b", "type": "function", "z": "1613373abaf77a2c", "name": "loadS", "func": "var file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\n\nif (data === 'no camera found'){\n msg.color = 'red'\n}\n\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 160, "wires": [ [ "8ebd1dcb5db156ed" ] ] }, { "id": "2415272f42ce468c", "type": "link in", "z": "1613373abaf77a2c", "name": "start status", "links": [ "6c6ef2255a7d39e5" ], "x": 55, "y": 160, "wires": [ [ "94a7aec739f9266b" ] ] }, { "id": "a1e14624058e74cd", "type": "link in", "z": "1613373abaf77a2c", "name": "start routine settings", "links": [ "960912e90ba5b5bc" ], "x": 55, "y": 500, "wires": [ [ "f4b3112a9ec6c487", "107a030938cbfea9", "84d6b96c8ebaac96", "9c6b48b7b4cc4e1a", "c470fd0b15356206", "9e30e33a1520fee0", "79ecb889f7113405" ] ] }, { "id": "1daf9e3a5bd5ab48", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "global.set('flag_pw', true)\nglobal.set('flag', true)\nmsg.enabled = true\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 920, "wires": [ [ "fb13752beddee9f2" ] ] }, { "id": "6d15f717d5a11002", "type": "function", "z": "1613373abaf77a2c", "name": "disable", "func": "msg.enabled = false\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 840, "wires": [ [ "e9b13dfd9f8d3711" ] ] }, { "id": "d14bbbb446d45e39", "type": "link in", "z": "1613373abaf77a2c", "name": "preview", "links": [ "f20da2fc4978b7bf" ], "x": 135, "y": 1260, "wires": [ [ "95439678bb2df2a2" ] ] }, { "id": "db7eea74d3bf892b", "type": "ui_toast", "z": "1613373abaf77a2c", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Finish", "cancel": "2nd pass", "raw": false, "className": "", "topic": "", "name": "", "x": 510, "y": 880, "wires": [ [ "0b8661103366f834" ] ] }, { "id": "0b8661103366f834", "type": "python3-function", "z": "1613373abaf77a2c", "name": "continue", "func": "from os import system\nfrom os.path import isfile\n\n\nif msg['payload'] == '2nd pass':\n msg['enabled'] = True\n return msg,None\n\nbasepath = '/home/pi/OpenScan/'\ntemppath = basepath + 'tmp/tmp.jpg'\nzippath = basepath + 'tmp/tmp.zip'\nprojectcode = msg['projectcode']\n\nsystem('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\n\nmsg['path'] = basepath + 'scans/' + projectcode + '.zip'\n\nif isfile(zippath):\n system('rm ' + zippath)\n\nreturn None, msg", "outputs": 2, "x": 660, "y": 920, "wires": [ [ "431f917c2541ae48", "579f2211199fd6ab", "c433515042ba01b5" ], [ "1daf9e3a5bd5ab48", "579f2211199fd6ab", "c433515042ba01b5" ] ] }, { "id": "79ecb889f7113405", "type": "python3-function", "z": "1613373abaf77a2c", "name": "inactive", "func": "from requests import get\nfrom OpenScan import load_int\n\ntimeout = load_int('timeout_ringlight')\n\nmsg['cmd'] = 'get'\n\ntry:\n flask = 'http://127.0.0.1:1312/ping'\n r = get(flask, params=msg)\n\n idle = float(r.text.split(\":\")[1].split('}')[0])\n\n msg['payload'] = idle\n\n if idle > timeout:\n return msg,msg\nexcept:\n pass\n\nreturn None,msg", "outputs": 2, "x": 200, "y": 740, "wires": [ [ "9e30e33a1520fee0" ], [ "8d7e04531c34f349" ] ] }, { "id": "8d7e04531c34f349", "type": "delay", "z": "1613373abaf77a2c", "name": "", "pauseType": "delay", "timeout": "30", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 200, "y": 780, "wires": [ [ "79ecb889f7113405" ] ] }, { "id": "c433515042ba01b5", "type": "ui_button", "z": "1613373abaf77a2c", "name": "pause", "group": "7aaf184330605300", "order": 18, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-pause", "payload": " ", "payloadType": "str", "topic": "Scan paused", "topicType": "str", "x": 810, "y": 1040, "wires": [ [ "63db399d8ac2acb6" ] ] }, { "id": "63db399d8ac2acb6", "type": "python3-function", "z": "1613373abaf77a2c", "name": "pause", "func": "from OpenScan import load_str, save\n\nstatus = load_str('status_internal_cam')\n\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\n return\n\nif status == 'Routine-paused':\n save('status_internal_cam', 'Routine-continue')\nelse:\n save('status_internal_cam', 'Routine-paused')", "outputs": 1, "x": 930, "y": 1040, "wires": [ [] ] }, { "id": "c8a3fde5206ce1ae", "type": "ui_template", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "name": "shutter", "order": 13, "width": 6, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 310, "y": 580, "wires": [ [ "44c3947a9b92d32d" ] ] }, { "id": "87be854db758a9a6", "type": "ui_template", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "name": "Cropy", "order": 16, "width": 6, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 310, "y": 660, "wires": [ [ "26f17a7f406df73c" ] ] }, { "id": "9daea4bd57f7a00e", "type": "ui_template", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "name": "Cropx", "order": 15, "width": 6, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 310, "y": 620, "wires": [ [ "c2b2ab5524271123" ] ] }, { "id": "cb6ebdabaaf7d0da", "type": "ui_template", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "name": "Photos", "order": 14, "width": 6, "height": 1, "format": "\n \n \n
\n
\n
\n {{sliderName}}\n
\n
\n \n \n \n
\n
\n
\n \n \n
\n
\n\n\n
", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 320, "y": 540, "wires": [ [ "ce28a0b5bfb0d5a1" ] ] }, { "id": "ea54fcc2.cfcc2", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "get dirs", "func": "from glob import glob\nimport os\nfrom zipfile import ZipFile\nfrom datetime import datetime\nfrom PIL import Image\n\ndef set_stats(stat):\n try:\n with open(directory+set[:-4]+\"/\"+stat,\"r\") as file:\n stat=file.read()\n except:\n stat=\"\"\n return stat\n\ntable=[]\ndirectory=\"/home/pi/OpenScan/scans/\"\n\nfor d in glob(directory+\"*.zip\"):\n set=os.path.basename(d)\n\n try:\n with ZipFile(d, 'r') as f:\n photos = len(f.namelist())\n \n if not os.path.isfile(directory + 'preview/' + os.path.basename(d)[:-4]+'.jpg'):\n image = f.open(f.namelist()[int(photos/2)])\n img = Image.open(image)\n width, height = img.size\n width_factor = width/300\n height_factor = height/295\n if height_factor>=width_factor and height_factor > 1:\n new_size=(int(width/height_factor), int(height/height_factor))\n img = img.resize(new_size)\n elif height_factor 1:\n new_size=(int(width/width_factor),int(height/width_factor))\n img = img.resize(new_size)\n img.save(directory + 'preview/' + os.path.basename(d)[:-4] +'.jpg')\n list=[]\n for fi in f.filelist:\n list.append(f.getinfo(fi.filename).date_time)\n \n duration = str(datetime(*max(list)) - datetime(*min(list)))\n \n size = float(int(float(os.path.getsize(d))/100000))/10\n size_full= os.path.getsize(d)\n status=set_stats(\"status\")\n expiration=set_stats(\"expiration\")\n download=set_stats(\"download\")\n \n if len(download)!=0:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Download\":\"RESULT\",\n \"Size_full\":size_full,\n \"Duration\":duration,\n })\n else:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Size_full\":size_full,\n \"Duration\":duration,\n\n })\n except:\n pass\n\nmsg['payload']=table\nmsg['topic']=\"\"\nreturn msg", "outputs": 1, "x": 480, "y": 180, "wires": [ [ "b9a3a0f9.bcbea", "f3662f8c7d3d7a2d" ] ] }, { "id": "2f4c0f98.dee2", "type": "link in", "z": "4981d84ef1a366d1", "name": "filelist", "links": [ "960912e90ba5b5bc", "a4f09e25.02569", "ed35109311335099", "fb13752beddee9f2" ], "x": 355, "y": 140, "wires": [ [ "ea54fcc2.cfcc2" ] ] }, { "id": "b9a3a0f9.bcbea", "type": "ui_table", "z": "4981d84ef1a366d1", "group": "b5fdd57b.15eda8", "name": "", "order": 1, "width": 13, "height": 7, "columns": [ { "field": "Date", "title": "Date", "width": "150", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Name", "title": "Name", "width": "150", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Photos", "title": "Photos", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Duration", "title": "ΔT", "width": "60", "align": "left", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Size", "title": "Size", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Status", "title": "Status", "width": "140", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } } ], "outputs": 1, "cts": true, "x": 610, "y": 180, "wires": [ [ "50710948.71c308", "4082b136.dae18", "834046a4.647938", "0c387c0291d6c131" ] ] }, { "id": "952ce286.4ffd4", "type": "ui_text", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "order": 3, "width": 6, "height": 1, "name": "Status", "label": "Status", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 270, "y": 60, "wires": [] }, { "id": "d4383424.7807c8", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "upload", "func": "import os\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\nfrom subprocess import getoutput\n\nbasedir = '/home/pi/OpenScan/'\n\nif load_str(\"feedback_terms\")==\"False\":\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic'] = 'OpenScanCloud - Terms of Use'\n return None,msg\n\nmsg = msg['payload']\n\ndef upload(filelist, ulinks):\n pid = getoutput('pidof curl')\n if pid != \"\":\n os.system('kill ' + pid)\n\n i = 0\n for file in filelist:\n link = ulinks[i]\n save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\n cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\"' + file + '\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\n i = i+1\n os.system(cmd)\n\n########\nif not os.path.isfile(basedir + 'settings/token'):\n msg['flag'] = True\n save('status_cloud', 'please enter token first')\n return msg\nwith open(basedir + 'settings/token', 'r') as file:\n token = file.read().strip('\\n')\n\n########\nr = OpenScanCloud('getTokenInfo', {'token':token})\n\nif r.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n save('status_cloud', 'invalid/missing token')\n return None,msg\nelif r.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nmsg1 = r.json()\n\n########\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\n msg['flag'] = True\n save('status_cloud', 'limit(s) exceeded')\n return msg\n\n########\ntemp = OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\nif temp.status_code not in (200,401):\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nif temp.status_code != 401:\n temp = temp.json()\n if 'status' in temp:\n if temp['status'] != 'created':\n with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\n file.write(temp['status'])\n msg['topic'] = \"Project already exists\"\n msg['payload'] = \"Do you want to re-upload the files?\"\n return None,None,msg\n#####\n\nmsg2={}\nmsg2['token'] = token\nmsg2['parts'] = 1\nmsg['partslist']=[]\n\n#######\nsize_to_split = load_int('osc_splitsize')\n\nif msg['Size_full'] > size_to_split:\n tempdir = basedir + 'tmp/split/'\n if os.path.isdir(tempdir):\n os.system('rm -r ' + tempdir)\n os.mkdir(tempdir)\n save('status_cloud', 'zipping files, please wait ...')\n cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\n os.system(cmd)\n save('status_cloud', 'zip done')\n list = os.listdir(tempdir)\n for l in list:\n msg['partslist'].append(tempdir + l)\n msg['partslist'].sort()\n msg2['parts']=len(msg['partslist'])\nelse:\n msg['partslist'] = [basedir + 'scans/' +msg['Set']]\n\n#######\nmsg2['photos'] = msg['Photos']\nmsg2['filesize'] = msg['Size_full']\nmsg2['project'] = msg['Set']\n\nr = OpenScanCloud('createProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nmsg1 = r.json()\n\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\n os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\n file.write('prepared')\n\nsave('status_cloud', 'uploading')\nupload(msg['partslist'], msg1['ulink'])\n\nr = OpenScanCloud('startProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Upload failed'\n msg['payload'] = 'please try again'\n save('status_cloud', 'upload failed')\n return None,msg\n\nsave('status_cloud', 'uploaded')\n\nsave('status_cloud', 'project started')\n\ntry:\n os.system('rm -r ' + tempdir)\nexcept:\n pass\n\nreturn msg", "outputs": 3, "x": 530, "y": 460, "wires": [ [ "9a132ab1.b21658" ], [ "3d16b3789632784d", "9a132ab1.b21658" ], [ "c1708277f79e61de" ] ] }, { "id": "50710948.71c308", "type": "change", "z": "4981d84ef1a366d1", "name": "set", "rules": [ { "t": "set", "p": "set", "pt": "global", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 750, "y": 180, "wires": [ [ "ada1b6f7cccc9344", "85839a17fb7b58b9" ] ] }, { "id": "834046a4.647938", "type": "ui_text", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "order": 4, "width": 6, "height": 1, "name": "Set", "label": "Set:", "format": "{{msg.payload.Name}}", "layout": "row-spread", "className": "", "x": 750, "y": 220, "wires": [] }, { "id": "9a132ab1.b21658", "type": "change", "z": "4981d84ef1a366d1", "name": "flag.true", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 780, "y": 440, "wires": [ [ "8689e938.dd9e38" ] ] }, { "id": "3c67e97b.9d19a6", "type": "function", "z": "4981d84ef1a366d1", "name": "enable", "func": "if (global.get('flag') === false){\n msg.enabled = false\n msg.color=\"white\"\n}\nelse{\n msg.enabled = true\n msg.color=\"red\"\n \n}\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 130, "y": 340, "wires": [ [ "7a93d1e18254685c", "e434ef42bd6b92e8", "d5d840183025d91b", "ab9e90ab5a53a0dd", "478994f671a3907d" ] ] }, { "id": "bfc01f26.c32cf", "type": "change", "z": "4981d84ef1a366d1", "name": "flag.false", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 420, "y": 520, "wires": [ [ "f20f2dbc.0f123" ] ] }, { "id": "b33d604c.5f1a6", "type": "link in", "z": "4981d84ef1a366d1", "name": "enable cloud", "links": [ "4082b136.dae18", "8689e938.dd9e38", "bd75f33b8a57c522", "e9b13dfd9f8d3711", "f20f2dbc.0f123", "fb13752beddee9f2" ], "x": 35, "y": 340, "wires": [ [ "3c67e97b.9d19a6" ] ] }, { "id": "f6bd1a04.470838", "type": "change", "z": "4981d84ef1a366d1", "name": "set", "rules": [ { "t": "set", "p": "payload", "pt": "msg", "to": "set", "tot": "global" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 410, "y": 460, "wires": [ [ "d4383424.7807c8" ] ] }, { "id": "4082b136.dae18", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "links": [ "b33d604c.5f1a6", "87574a42938afec4" ], "x": 715, "y": 140, "wires": [] }, { "id": "f20f2dbc.0f123", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 525, "y": 520, "wires": [] }, { "id": "8689e938.dd9e38", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 875, "y": 440, "wires": [] }, { "id": "15de0ebb.616d61", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 380, "wires": [ [ "a7d89487.ee8858" ] ] }, { "id": "a7d89487.ee8858", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\ntry:\n os.remove(dir+msg['Set'])\n shutil.rmtree(dir+msg['Set'][:-4])\nexcept:\n pass\nreturn msg", "outputs": 1, "x": 690, "y": 380, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "a4f09e25.02569", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "links": [ "2f4c0f98.dee2", "c20357dd.374108", "e9aab326.a6896", "edd22cc7.befe1", "19b81967.49db87", "8ee1b3bb.7b0b3", "d5246b3cc796afc6" ], "x": 775, "y": 360, "wires": [] }, { "id": "7a93d1e18254685c", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "92c98e6ce7cd25f9" ], "x": 235, "y": 580, "wires": [] }, { "id": "4d99c601c9881680", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "refresh", "func": "from time import sleep\nimport os\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\n\nbasepath = '/home/pi/OpenScan/scans/'\n\nif load_bool(\"terms\")==False:\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic']='OpenScanCloud - Terms of Use'\n return None,msg\n\nsave('status_cloud','refreshing')\ntoken = load_str('token')\n\ntest = OpenScanCloud('getTokenInfo',{'token':token})\nif test.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n return None,msg\nelif test.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nstats = test.json()\nfor i in stats:\n save('osc_'+i, stats[i])\n pass\n\nmsg={}\nprojects = []\nfor i in os.listdir(basepath):\n if i == 'preview':\n continue\n if os.path.isdir(basepath + i):\n if os.path.isfile(basepath + i + '/status'):\n with open(basepath + i + '/status', 'r') as file:\n status = file.read().strip('\\n')\n if status in ['expired', 'processing done', 'processing failed']:\n continue\n projects.append(i)\n\nfor p in projects:\n r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\n if r.status_code == 200:\n answer = r.json()\n if answer == {}:\n os.system('rm -r ' + basepath + p)\n else:\n with open(basepath + p + '/status', 'w+') as file:\n file.write(answer['status'])\n with open(basepath + p + '/download', 'w+') as file:\n file.write(answer['dlink'])\n\nmsg['list'] = projects\nsleep(0.5)\nsave('status_cloud','ready')\nreturn msg, None\n", "outputs": 2, "x": 320, "y": 180, "wires": [ [ "ea54fcc2.cfcc2", "b42e061fb1f1f3d7" ], [ "6434e713f088012b" ] ] }, { "id": "372e95797a3f2f3b", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "limit :)", "func": "from time import sleep\n\nmsg2={}\nmsg2['enabled'] = True\n\nmsg['enabled'] = False\nnode.send(msg)\n\nwait = 15\n\nfor i in range (wait):\n msg['text'] = ' ('+ str(wait - i)+')'\n node.send(msg)\n\nmsg['enabled'] = True\nmsg['text']=\"\"\n\n\nreturn msg", "outputs": 1, "x": 90, "y": 220, "wires": [ [ "573edbfdb7500ddc" ] ] }, { "id": "573edbfdb7500ddc", "type": "delay", "z": "4981d84ef1a366d1", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 230, "y": 220, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "dacb1f078b624e10", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 340, "wires": [ [ "c8d65cc7c2ff7c36" ] ] }, { "id": "92c98e6ce7cd25f9", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "7a93d1e18254685c", "bd75f33b8a57c522" ], "x": 35, "y": 180, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "3d16b3789632784d", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 770, "y": 480, "wires": [ [] ] }, { "id": "6434e713f088012b", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 470, "y": 220, "wires": [ [] ] }, { "id": "c8d65cc7c2ff7c36", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\nfor i in os.listdir(dir):\n if os.path.isdir(dir + i):\n shutil.rmtree(dir + i)\n else:\n os.remove(dir + i)\n\nos.mkdir(dir + \"preview\")\n\nreturn msg", "outputs": 1, "x": 690, "y": 340, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "f4e9a4bd79b4221f", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 340, "wires": [ [ "dacb1f078b624e10" ] ] }, { "id": "2806bf08ea21216d", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.Set=global.get('set')['Set']\nmsg.payload = 'Are you sure to delete ' + msg.Set + '?'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 380, "wires": [ [ "15de0ebb.616d61" ] ] }, { "id": "61990987acd0f263", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "6c6ef2255a7d39e5" ], "x": 45, "y": 60, "wires": [ [ "51579603bce21e98" ] ] }, { "id": "e8e488a6dd5d0b33", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "Download", "order": 6, "width": 3, "height": 1, "format": "\n
Download\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 880, "y": 260, "wires": [ [] ] }, { "id": "0c387c0291d6c131", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.download = '/scans/' + String(msg.payload.Set)\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 750, "y": 260, "wires": [ [ "e8e488a6dd5d0b33" ] ] }, { "id": "e5f38b4a07a5e278", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "960912e90ba5b5bc" ], "x": 655, "y": 220, "wires": [ [ "834046a4.647938" ] ] }, { "id": "e434ef42bd6b92e8", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "upload2", "order": 7, "width": 3, "height": 1, "format": "upload", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 460, "wires": [ [ "f6bd1a04.470838", "bfc01f26.c32cf" ] ] }, { "id": "c46e10b9c201913e", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "refresh", "order": 1, "width": 3, "height": 1, "format": "refresh{{msg.text}}", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 160, "y": 180, "wires": [ [ "372e95797a3f2f3b", "4d99c601c9881680" ] ] }, { "id": "d5d840183025d91b", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "del set", "order": 9, "width": 2, "height": 1, "format": "delete set", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 380, "wires": [ [ "2806bf08ea21216d" ] ] }, { "id": "ab9e90ab5a53a0dd", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "del ", "order": 10, "width": 2, "height": 1, "format": "delete all", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 340, "wires": [ [ "f4e9a4bd79b4221f" ] ] }, { "id": "478994f671a3907d", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "combine", "order": 8, "width": 2, "height": 1, "format": "combine", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 620, "wires": [ [ "51bfd0fb7b1d292e" ] ] }, { "id": "189c1eed09624a7b", "type": "function", "z": "4981d84ef1a366d1", "name": "combine", "func": "combine = global.get('combine')\ncombine_set = global.get('set').Set\n\nif (combine === true && global.get('combine_set') !== combine_set){\n msg.set1 = global.get('combine_set')\n msg.set2 = combine_set\n global.set('combine', false)\n msg.topic = 'Combine the following two sets:'\n msg.payload = msg.set1 + '
' + msg.set2 + '
FILES WILL BE MERGED INTO ON FILE!'\n return msg\n}\nglobal.set('combine_set' , combine_set)\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 660, "wires": [ [ "1493398979a63775" ] ] }, { "id": "51bfd0fb7b1d292e", "type": "function", "z": "4981d84ef1a366d1", "name": "combine", "func": "global.set('combine', true)\ncombine_set = global.get('set').Set\nmsg.topic = 'Merge two sets into one (can not be undone)!'\nmsg.payload = combine_set\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 420, "y": 620, "wires": [ [] ] }, { "id": "da325be8e74179be", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "combine", "func": "from os.path import getsize\nfrom shutil import copy\nfrom os import rename, remove\nimport zipfile as z\nfrom OpenScan import save\n\nfrom time import sleep\n\nif msg['payload'] != 'OK':\n return\n\nbasepath = '/home/pi/OpenScan/scans/'\ntmp1 = basepath + msg['set1']\ntmp2 = basepath + msg['set2']\n\nif getsize(tmp1) > getsize(tmp2):\n set1 = tmp1\n set2 = tmp2\nelse:\n set1 = tmp2\n set2 = tmp1\n\nzips = [set1, set2]\n\nwith z.ZipFile(set1, 'a') as z1:\n z2 = z.ZipFile(set2, 'r')\n i = 0\n for n in z2.namelist():\n i += 1\n n2 = n\n save('status_cloud','writing ' + str(i) + '/' + str(len(z2.namelist())))\n while 'X'+n in z1.namelist():\n n = 'X' + n\n z1.writestr('X'+n, z2.open(n2).read())\nsave('status_cloud','ready')\n\nos.rename(set1, set1[:-4] + 'X.zip')\nos.remove(set2)\n\nreturn msg", "outputs": 1, "x": 560, "y": 660, "wires": [ [ "ed35109311335099" ] ] }, { "id": "ed35109311335099", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "2f4c0f98.dee2" ], "x": 655, "y": 660, "wires": [] }, { "id": "1493398979a63775", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "Combine", "x": 420, "y": 660, "wires": [ [ "da325be8e74179be" ] ] }, { "id": "ada1b6f7cccc9344", "type": "link out", "z": "4981d84ef1a366d1", "name": "combine", "mode": "link", "links": [ "6dd356510c446cf4" ], "x": 835, "y": 180, "wires": [] }, { "id": "6dd356510c446cf4", "type": "link in", "z": "4981d84ef1a366d1", "name": "combine", "links": [ "ada1b6f7cccc9344" ], "x": 175, "y": 660, "wires": [ [ "189c1eed09624a7b" ] ] }, { "id": "b42e061fb1f1f3d7", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "397ab7f44b893c89" ], "x": 435, "y": 140, "wires": [] }, { "id": "b99505440832439f", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "diskspace", "func": "from subprocess import getoutput\nfrom OpenScan import load_int\n\ndiskspace_threshold = load_int('diskspace_threshold')\n\ndiskspace = getoutput('df -h / | awk \"{print $5}\"').split('\\n')[1]\n\navailable = int(float(diskspace.replace(' ','').split('G')[2])*1000)\n\n\nif available < diskspace_threshold:\n msg['topic'] = 'Low diskspace remaining! ('+str(available)+'MB)' \n msg['payload'] = 'Please delete some/all locally stored files.'\n msg['color'] = 'red'\n return msg\n", "outputs": 1, "x": 800, "y": 100, "wires": [ [ "92047434f8e9f927" ] ] }, { "id": "92047434f8e9f927", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 950, "y": 100, "wires": [ [] ] }, { "id": "f3662f8c7d3d7a2d", "type": "delay", "z": "4981d84ef1a366d1", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "minute", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "allowrate": false, "outputs": 1, "x": 650, "y": 100, "wires": [ [ "b99505440832439f" ] ] }, { "id": "51579603bce21e98", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "read", "func": "from OpenScan import load_str\nfrom os import listdir, path\n\nstatus = load_str('status_cloud')\n\nif status[0:9] == 'uploading':\n progress = load_str('status_uploadprogress')[-6:]\n if progress[-1:] == '%':\n status = status + ' (' + progress + ')'\n\nif status[0:7] == 'zipping':\n path1 = '/home/pi/OpenScan/tmp/split/'\n files = listdir(path1)\n size1 = 0\n for file in files:\n size1 += path.getsize(path1+file)\n size2 = path.getsize('/home/pi/OpenScan/scans/'+ files[0][:-2])\n \n status = 'zipping files (' + str(float(int(1000*size1/size2))/10) + '%)'\n\nmsg['status'] = status\nreturn msg\n", "outputs": 1, "x": 130, "y": 60, "wires": [ [ "952ce286.4ffd4" ] ] }, { "id": "9a5baae623355f9d", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "preview", "order": 5, "width": 6, "height": 6, "format": "
\n\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 1020, "y": 220, "wires": [ [] ] }, { "id": "85839a17fb7b58b9", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "preview", "func": "from time import time\nimport os\n\npath = '/home/pi/OpenScan/scans/preview/'\nimage = os.path.basename(msg['payload']['Set'])[:-4] +'.jpg'\n\nmsg['payload']=\"/scans/preview/\" + image +\"?ts=\"+str(int(time()*10))\nreturn msg", "outputs": 1, "x": 880, "y": 220, "wires": [ [ "9a5baae623355f9d" ] ] }, { "id": "45058bfcf047e8cc", "type": "inject", "z": "4981d84ef1a366d1", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 100, "y": 120, "wires": [ [] ] }, { "id": "c1708277f79e61de", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Yes", "cancel": "No", "raw": false, "className": "", "topic": "", "name": "resetProject?", "x": 790, "y": 520, "wires": [ [ "98a45dd77f31c8b4" ] ] }, { "id": "98a45dd77f31c8b4", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "resetProject", "func": "from OpenScan import OpenScanCloud\n\nif msg['payload'] == \"No\":\n return\n\nwith open('/home/pi/OpenScan/settings/token', 'r') as file:\n token = file.read().strip('\\n')\n\nOpenScanCloud('resetProject', {'token':token, 'project':msg['Set']})\nreturn msg", "outputs": 1, "x": 950, "y": 520, "wires": [ [ "f6bd1a04.470838" ] ] }, { "id": "40dee936a9abac0d", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "SSH", "tooltip": "", "group": "4fe6b4c0ade0938a", "order": 3, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 390, "y": 340, "wires": [ [ "dc354c54078ca607" ] ] }, { "id": "4fd9bb53fdb51a25", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Samba", "tooltip": "", "group": "4fe6b4c0ade0938a", "order": 4, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "test2", "topicType": "msg", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 400, "y": 380, "wires": [ [ "b0aa8ffae5a3578a" ] ] }, { "id": "dc354c54078ca607", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "ssh", "func": "from OpenScan import load_bool, save\n\nstate = msg['payload']\n\nif state != load_bool('ssh'):\n save('ssh', state)\n\nif state == True:\n os.system('/etc/init.d/ssh start')\nelse:\n os.system('/etc/init.d/ssh stop')", "outputs": 1, "x": 530, "y": 340, "wires": [ [] ] }, { "id": "52858b4eceacc902", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "", "group": "4fe6b4c0ade0938a", "order": 2, "width": 6, "height": 1, "passthru": false, "label": "Terms Of Use", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 120, "y": 300, "wires": [ [ "f99ec8781a33ec7d" ] ] }, { "id": "595153429adef33b", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Wifi", "group": "0fe66c9190b8a87c", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Network Settings

Hostname

The device can be accessed through any browser in the same network. Therefore, you can either enter the device's IP address or the given hostname. The standard name is 'openscan' but it is highly recommended to change the name, when using multiple devices (e.g. 'openscan1', 'openscan2' ...)

Select Wifi

After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: ISO 3166 Country Code on Wikipedia

Search Wifi

You can manually refresh the list of available networks by pressing this button.

Reset Wifi

Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see glennklockwood.com for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 590, "y": 120, "wires": [ [ "f304680180a23479" ] ] }, { "id": "7dc39bd847d16ded", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Agree", "cancel": "Disagree", "raw": true, "className": "", "topic": "", "name": "", "x": 410, "y": 300, "wires": [ [ "5f849178998d9082" ] ] }, { "id": "02858034e17b827f", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "General", "group": "4fe6b4c0ade0938a", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

General Settings

Terms Of Use

In order to use the OpenScanCloud, please read the terms of use as files will be transmitted from your device to the OpenScan Servers.

SSH

SSH can be used to access the Raspberry Pi and modify core files of the operating system. Please deactivate, if you do not want to use this feature.

If you want to use it, the default user is pi, password: raspberry. Please change the password immediately. 

Samba

Samba s a network local file sharing server, which allows accessing the Raspberry Pi's file system through the explorer (and other programs like FileZilla). You can use it to transfer custom photo sets to the device in order to use the OpenScanCloud. Therefore, you need to transfer the zip file containing your photos to the following folder /OpenScan/scans/

You can access the Raspberry Pis file system by inserting the following line into your Windows explorer: 

\\\\OpenScan/PiShare/OpenScan/scans/

username: pi, password: raspberry

Please deactivate the local file sharing if you do not intend to use it

Advanced Settings

Enable a ton of additional settings, which should be changed only if you know what you are doing ;)

Model

Device model you are using: OpenScan Mini or OpenScan Classic. Setting the device affects the settings of the motor (gear ratio, acceleration, speed). You can change those values manually in the advanced settings.

Camera

A wide range of camera modules is supported (Pi camera v1.3, v2.1, HQ, Arducam IMX519, IMX290, IMX378, OV9281). If you encounter any issues with those models, please check the orientation of the camera ribbon cable and its connectors.

DSLR (gphoto) - connect a wide range of DSLR cameras to the device through USB. See GPhoto for a full list of supported devices.

External camera - triggering any camera through an isolated GPIO signal on the front side of the pi shield.

Shutdown/Reboot

Always use the shutdown button before you power off your Raspberry Pi.

Restore Default Settings

In case you want to restore the default settings

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 740, "y": 240, "wires": [ [ "f304680180a23479" ] ] }, { "id": "675d4933a44ae6b5", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Pinout", "group": "644b3bcc903d46ca", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Pinout

ONLY CHANGE THE PINOUT IF YOU ARE ABSOLUTELY SURE! CHANGES CAN DAMAGE THE RASPBERRY PI AND ANY PERIPHERALS!


", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 430, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "b0aa8ffae5a3578a", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "smb", "func": "from OpenScan import load_bool, save\n\nstate = msg['payload']\n\nif state != load_bool('smb'):\n save('smb', state)\nif state == True:\n os.system('/etc/init.d/smbd start')\nelse:\n os.system('/etc/init.d/smbd stop')\n\n\n", "outputs": 1, "x": 530, "y": 380, "wires": [ [] ] }, { "id": "cc3cb10f2ea3f8b8", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "blink Light1", "func": "import RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nfrom OpenScan import ringlight\nfrom time import sleep\n\ndelay = 0.1\nringlight(2,False)\n\nfor i in range (5):\n ringlight(1,True)\n sleep(delay)\n ringlight(1,False)\n sleep(delay)", "outputs": 1, "x": 290, "y": 760, "wires": [ [] ] }, { "id": "d114f4d4d7f31981", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "reboot", "func": "import os\nfrom time import sleep\nsleep(1.5)\nimport RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nos.system('reboot -h')\n", "outputs": 1, "x": 270, "y": 720, "wires": [ [] ] }, { "id": "79181ad3b56d5c62", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "4fe6b4c0ade0938a", "order": 7, "width": 2, "height": 1, "name": "", "label": "Model", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 730, "y": 620, "wires": [] }, { "id": "4d81bd138733c410", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "4fe6b4c0ade0938a", "order": 9, "width": 2, "height": 1, "name": "", "label": "Camera", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 840, "y": 420, "wires": [] }, { "id": "80b579a4220e5c23", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "model", "label": "", "tooltip": "", "place": "Select option", "group": "4fe6b4c0ade0938a", "order": 8, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Please Select", "value": "None", "type": "str" }, { "label": "OpenScan Mini", "value": "OSMini", "type": "str" }, { "label": "OpenScan Classic", "value": "OSClassic", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 390, "y": 620, "wires": [ [ "896242c5a7e50fa7" ] ] }, { "id": "a2c1dba3e67be015", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "Camera", "label": "", "tooltip": "", "place": "Select option", "group": "4fe6b4c0ade0938a", "order": 10, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Pi Cam v1 - 5mp", "value": "ov5647", "type": "str" }, { "label": "Pi Cam v2 - 8mp", "value": "imx219", "type": "str" }, { "label": "Pi Cam HQ - 12.3mp", "value": "imx477", "type": "str" }, { "label": "Arducam IMX519 - 16mp", "value": "imx519", "type": "str" }, { "label": "IMX290 a", "value": "imx290a", "type": "str" }, { "label": "IMX290 b", "value": "imx290b", "type": "str" }, { "label": "IMX378", "value": "imx378", "type": "str" }, { "label": "OV9281", "value": "ov9281", "type": "str" }, { "label": "DSLR (gphoto)", "value": "gphoto", "type": "str" }, { "label": "USB Webcam", "value": "usb_webcam", "type": "str" }, { "label": "External Camera", "value": "external", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 400, "y": 420, "wires": [ [ "4058a31e942e8f95", "6d68cccec646e0a0" ] ] }, { "id": "9cf5d56263caada7", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Motor", "group": "d49a6dfd7fb17096", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Motor Settings

Turntable Mode

Activate turntable mode in order to deactivate the rotor. The routine will only move the turntable and take a given number of photos.

Rotor - Start Angle, Min and Max Angle

Since this version of OpenScan does not have an endstop (yet), it is necessary to tell the device its position when the routine is being started. 0° corresponds to the horizontal (natural) orientation.

After that, the device will equally space the image positions between angle min and angle max.

Rotor/Turntable

Steps per rotation -  defines the number of steps it takes to move the axis 360°. It is defined by A*B*C, where A is the number of steps for one revolution of the given stepper motor (normally 200), B is the microstepping used (normally 16), and C the gear ratio (1 for the turntable and 15 or 5,33 for the OpenScan Mini and Classic respectively)

Delay - time in microseconds between each step of the motor. Lower this value if the movement is too fast

Acceleration - a factor defining how fast the delay time between each step is being changed during acceleration and deceleration phases. Lower this value in order to make the movement smoother.

Acceleration ramp - the number of steps allowed for the acceleration processes. Increase this value, if you want smoother movement.

Manual Angle - Defines the degree value for the manual movement through the arrow buttons in the scan menu

Direction - If needed, reverse the movement (in case the arrow buttons and movement do not correspond). Alternatively, you can flip the motor cable 180° (BUT MAKE SURE TO POWER OFF THE DEVICE!)


", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 430, "y": 120, "wires": [ [ "f304680180a23479" ] ] }, { "id": "72238e6a01d1152c", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "camera", "group": "93aadb71dee6d977", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Camera Settings

Jpeg quality

Value in percent, which usually does not need to be changed.

Downscale Preview

The preview image has to be scaled down depending on your network speed. If you want to have a higher quality preview image, you can increase this value, which defines the maximal width/height value. If the value is too high, the preview window might not update

Image Rotation

Change the image rotation, if needed.

Timeout

Defines the time in seconds, when the libcamera command (used for the camera modules) will timeout. Increase this value, if the camera does not get triggered in each position.

Delay Before/After

A fixed delay in seconds before and/or after a photo is taken. Increase this value when the photos have visual motion blur.

AWBG, Gain, Contrast, Saturation

Under most circumstances, you do not need to touch these values.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 420, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "15a0a2f431ce55c3", "type": "comment", "z": "017bd4e4a428bee5", "name": "General Settings", "info": "", "x": 120, "y": 260, "wires": [] }, { "id": "87a403b9a09aa38d", "type": "comment", "z": "017bd4e4a428bee5", "name": "Network", "info": "", "x": 100, "y": 880, "wires": [] }, { "id": "896242c5a7e50fa7", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "model", "func": "from OpenScan import load_str, save\n\nstate = msg['payload']\nmsg['state'] = state\n\nif state != load_str('model'):\n save('model', state)\n if state == 'OSMini':\n save('rotor_stepsperrotation',48000)\n save('cam_rotation',90)\n save('rotor_anglemin',-70)\n save('rotor_anglemax',20)\n \n\n if state == 'OSClassic':\n save('rotor_stepsperrotation',17067)\n save('cam_rotation',0)\n save('rotor_anglemin',-30)\n save('rotor_anglemax',30)\n\nif state == \"OSMini\":\n msg['crop2'] = 'Crop X (%)'\n msg['crop1'] = 'Crop Y (%)'\nelif state == \"OSClassic\":\n msg['crop1'] = 'Crop X (%)'\n msg['crop2'] = 'Crop Y (%)'\n\nreturn msg", "outputs": 1, "x": 530, "y": 620, "wires": [ [ "f358de1e64b491bb" ] ] }, { "id": "4058a31e942e8f95", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "camera", "func": "from OpenScan import load_str, save\nfrom json import load\nstate = msg['payload']\nstate_old = load_str('camera')\n\nif state_old != state:\n save('camera',state)\n return msg", "outputs": 1, "x": 540, "y": 500, "wires": [ [ "34b685aff2080d31" ] ] }, { "id": "c833f6243a059d83", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "more sets", "label": "Advanced Settings", "tooltip": "", "group": "4fe6b4c0ade0938a", "order": 5, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 400, "y": 660, "wires": [ [ "8be8015931c663cc" ] ] }, { "id": "15fd1c9e5610cb85", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "more sets", "func": "from OpenScan import save\n\nif msg['payload'] != 'OK':\n msg['payload'] = False\n return None,msg\n \nsave('advanced_settings', True)\n\nreturn msg", "outputs": 2, "x": 820, "y": 660, "wires": [ [ "62cd775a1c02dac8" ], [ "c833f6243a059d83" ] ] }, { "id": "74c5c7cd2681045b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "load camera&model", "func": "from OpenScan import load_str, load_bool\n\nmodel = load_str('model')\ncamera = load_str('camera')\nupdate = load_bool('updateable')\nmsg['model'] = model\nmsg['camera'] = camera\nmsg2 = {}\nmsg3 = {}\nmsg4 = {}\n\nif camera in ('imx219','ov5647','imx477','imx290a','imx290b','imx378','ov9281','gphoto'):\n msg['payload'] = {\"group\":{\"hide\":[\"Scan_Arducam\"],\"show\":[\"Scan_Settings\",\"Scan_Picamera\"]}}\nelif camera in ('imx519'):\n msg['payload'] = {\"group\":{\"hide\":[\"Scan_Picamera\"],\"show\":[\"Scan_Settings\",\"Scan_Arducam\"]}}\nelif camera in ('external'):\n msg['payload'] = {\"group\":{\"hide\":[\"Scan_Arducam\",\"Scan_Picamera\"],\"show\":[\"Scan_Settings\"]}}\n\n\nif model == 'None' or model == '' or camera == 'None' or camera == '':\n msg2['payload']={\"tabs\": {\"hide\": [\"Scan\", \"Files&Cloud\",\"Settings\",\"Update & Info\"]}}\n msg3['payload'] = {\"group\":{\"hide\":[\"OpenScan_Home\"],\"show\":[\"OpenScan_Initialize\"]}}\nelse:\n msg2['payload']={\"tabs\": {\"show\": [\"Scan\", \"Files&Cloud\",\"Settings\",\"Update & Info\"]},\"hide\":{}}\n msg3['payload'] = {\"group\":{\"show\":[\"OpenScan_Home\"],\"hide\":[\"OpenScan_Initialize\"]}}\n\nif update == True:\n msg4['payload'] = {\"group\":{\"show\":[\"OpenScan_Update\"]}}\nelif update == False:\n msg4['payload'] = {\"group\":{\"hide\":[\"OpenScan_Update\"]}}\n\nreturn msg,msg2,msg3,msg4", "outputs": 4, "x": 340, "y": 40, "wires": [ [ "b4db790aad28ba39" ], [ "b4db790aad28ba39" ], [ "b4db790aad28ba39" ], [ "b4db790aad28ba39" ] ] }, { "id": "b4db790aad28ba39", "type": "ui_ui_control", "z": "017bd4e4a428bee5", "name": "change visibility", "events": "all", "x": 600, "y": 40, "wires": [ [] ] }, { "id": "eb8ccf2786ea3d63", "type": "inject", "z": "017bd4e4a428bee5", "name": "1s_repeater", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "1", "crontab": "", "once": true, "onceDelay": "2", "topic": "", "payload": "", "payloadType": "date", "x": 150, "y": 40, "wires": [ [ "74c5c7cd2681045b", "9b756a1f9b0e7317" ] ] }, { "id": "9b756a1f9b0e7317", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "load advanced", "func": "from OpenScan import load_bool\n\nif load_bool('advanced_settings') == False:\n msg['payload']={\"group\":{\"hide\":[\"Settings_Camera\",\"Settings_Motor\",\"Settings_Pinout\",],\"show\":[\"Settings_General\",\"Settings_Network\",\"Settings_OpenScanCloud\"]}}\nelse:\n msg['payload']={\"group\":{\"hide\":[],\"show\":[\"Settings_General\",\"Settings_Network\",\"Settings_OpenScanCloud\",\"Settings_Camera\",\"Settings_Motor\",\"Settings_Pinout\",]}}\nreturn msg", "outputs": 1, "x": 320, "y": 80, "wires": [ [ "b4db790aad28ba39" ] ] }, { "id": "ca4afadb5b21751f", "type": "comment", "z": "017bd4e4a428bee5", "name": "Info Texts", "info": "", "x": 100, "y": 120, "wires": [] }, { "id": "f393400.d87dcc", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "check ip address", "func": "import socket\nimport subprocess\n\ntestIP = \"8.8.8.8\"\ns = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\ns.connect((testIP, 0))\nipaddr = s.getsockname()[0]\nhost = socket.gethostname()\n\nmsg['ip']=ipaddr\n\nreturn msg", "outputs": 1, "x": 410, "y": 1060, "wires": [ [ "bb789eed.9f73c" ] ] }, { "id": "bb789eed.9f73c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "0fe66c9190b8a87c", "order": 2, "width": 0, "height": 0, "name": "", "label": "Your local IP:", "format": "{{msg.ip}}", "layout": "row-spread", "className": "", "x": 590, "y": 1060, "wires": [] }, { "id": "2a0f9919.4c9a86", "type": "comment", "z": "017bd4e4a428bee5", "name": "OpenScanCloud", "info": "", "x": 120, "y": 1240, "wires": [] }, { "id": "27c6b221c90ed9e1", "type": "exec", "z": "017bd4e4a428bee5", "command": "iwlist wlan0 scan | grep ESSID | sed 's/ESSID://g;s/\"//g;s/^ *//;s/ *$//'", "addpay": false, "append": "", "useSpawn": "false", "timer": "", "winHide": false, "oldrc": false, "name": "scan", "x": 250, "y": 1040, "wires": [ [ "b05cf92302a5c112", "f393400.d87dcc" ], [ "e9677b85856b5873" ], [] ] }, { "id": "b05cf92302a5c112", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "WIFI", "func": "msg['options']=[]\n\nfor i in msg['payload'].split('\\n'):\n if i not in msg['options'] and i!=\"\":\n msg['options'].append(i)\n \nif len(msg['options']) != 0:\n msg['enabled']=True\n\nreturn msg", "outputs": 1, "x": 370, "y": 1020, "wires": [ [ "59c9f67283ba1709" ] ] }, { "id": "da5ddaf4cc25b8c8", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "search", "group": "0fe66c9190b8a87c", "order": 4, "width": 3, "height": 1, "passthru": false, "label": "Search Wifi", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "true", "payloadType": "bool", "topic": "", "topicType": "str", "x": 90, "y": 980, "wires": [ [ "27c6b221c90ed9e1", "51521bc6eb44cde5" ] ] }, { "id": "59c9f67283ba1709", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "", "label": "", "tooltip": "", "place": "Select Wifi", "group": "0fe66c9190b8a87c", "order": 3, "width": 6, "height": 1, "passthru": true, "multiple": false, "options": [], "payload": "", "topic": "", "topicType": "str", "className": "", "x": 520, "y": 980, "wires": [ [ "2bb52656f9554dab" ] ] }, { "id": "b2d7d6a730f7dca6", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Reset Wifi", "group": "0fe66c9190b8a87c", "order": 5, "width": 3, "height": 1, "passthru": false, "label": "Reset Wifi", "tooltip": "", "color": "red", "bgcolor": "", "className": "", "icon": "", "payload": "Delete all prior wifi connections? (You will need to reconnect to the OpenScan device by Ethernet or manually modify the wpa_supplicant.conf)", "payloadType": "str", "topic": "", "topicType": "str", "x": 110, "y": 1140, "wires": [ [ "78985ac6d3bcdf60" ] ] }, { "id": "c3b8faac9ebb2c80", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Reset Wifi", "func": "from time import sleep\n\nif msg['payload']!=\"Yes\":\n return\n\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\nwith open(temp_dir, 'w+') as file:\n file.write('update_config=1\\nctrl_interface=DIR=/var/run/wpa_supplicant\\ncountry=de\\n\\n')\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\nos.system('wpa_cli -i wlan0 reconfigure')\nsleep(3)\nos.system('systemctl restart nodered')\nreturn msg", "outputs": 1, "x": 440, "y": 1140, "wires": [ [] ] }, { "id": "78985ac6d3bcdf60", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 270, "y": 1140, "wires": [ [ "c3b8faac9ebb2c80" ] ] }, { "id": "4f7f49b12c2d2572", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "add Wifi", "func": "from time import sleep\nsleep(0.1)\n\nos.system('wpa_cli -i wlan0 reconfigure')\n\nreturn msg", "outputs": 1, "x": 1320, "y": 1000, "wires": [ [] ] }, { "id": "ebcc98685059b9d4", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "password", "x": 780, "y": 980, "wires": [ [ "68204a14528ab842" ] ] }, { "id": "68204a14528ab842", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "if msg['payload'] == 'Cancel':\n return\n\nmsg['password'] = msg['payload']\nmsg['payload']='Enter country code (ISO 3166-1 alpha-2, see: Wikipedia)'\n\n\nreturn msg", "outputs": 1, "x": 910, "y": 980, "wires": [ [ "852edf901bdec9c5" ] ] }, { "id": "852edf901bdec9c5", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Save", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "country", "x": 1040, "y": 980, "wires": [ [ "1b09d634e3d9357b" ] ] }, { "id": "1b09d634e3d9357b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "modWPA", "func": "if msg['payload'] == 'Cancel':\n return\n\nif len(msg['payload'])!=2:\n msg['payload'] = 'invalid country code'\n return msg,None\n\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\ntemp_dir = '/home/pi/OpenScan/tmp/wpa'\n\ncode = msg['payload'].upper()\nssid = msg['ssid']\npassword = msg['password']\n\nif len(code) != 2:\n msg['topic'] = 'ERROR'\n msg['payload'] = 'invalid country code (see ISO 3166-1 alpha-2)'\n return msg\n\nwith open(wpa_dir, 'r') as file:\n for i in file.readlines():\n if 'country=' in i:\n code_old=i.split('country=')[1][0:2]\n break\n\nwith open(wpa_dir, 'r') as file:\n wpa = file.read()\n if ssid in wpa:\n msg['topic'] = 'ERROR'\n msg['payload'] = 'Network already exists! If you have trouble connecting, please consider resetting the saved Wifi connections.'\n return msg\n wpa=wpa.replace('country=' + code_old, 'country=' + code)\n wpa=wpa + '\\nnetwork={\\n priority=10\\n ssid=\"'+ssid+'\"\\n psk=\"'+password+'\"\\n}\\n'\n\nwith open(temp_dir,'w+') as file:\n file.write(wpa)\nos.system('mv '+temp_dir + ' ' + wpa_dir)\n\nmsg['topic'] = 'Updating Wifi'\nmsg['payload'] = 'reconnecting might take a moment'\nreturn msg,msg\n", "outputs": 2, "x": 1180, "y": 980, "wires": [ [ "03732a7d3b0c95aa" ], [ "4f7f49b12c2d2572" ] ] }, { "id": "03732a7d3b0c95aa", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 1330, "y": 960, "wires": [ [] ] }, { "id": "e97d17c6590138e2", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Cloud-settings", "group": "3b4bd36726be16d5", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

OpenScanCloud

OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at BuyMeACoffee.

The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.

Register

In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.

Enter Token

Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact cloud@openscan.eu

Token

A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests cloud@openscan.eu

Credit (GB)

Each token comes with a given amount of 'credit' which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers. 

IMPORTANT: The credit can be increased at any time by sending a (nice) mail to cloud@openscan.eu

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 620, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "f7bf47e3eec6d736", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 4, "width": 0, "height": 0, "name": "", "label": "Max. Number of Photos:", "format": "{{msg.limit_photos}}", "layout": "row-spread", "className": "", "x": 410, "y": 1380, "wires": [] }, { "id": "b52d91c628b151a4", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 5, "width": 0, "height": 0, "name": "", "label": "Max. Filesize (GB):", "format": "{{msg.limit_filesize}}", "layout": "row-spread", "className": "", "x": 390, "y": 1420, "wires": [] }, { "id": "1969c709ef2fd1d5", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 3, "width": 0, "height": 0, "name": "", "label": "Credit (GB):", "format": "{{msg.credit}}", "layout": "row-spread", "className": "", "x": 370, "y": 1460, "wires": [] }, { "id": "88e92b621d2a3394", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "save", "func": "import requests\nimport os\nfrom OpenScan import save, OpenScanCloud\n\nif msg['payload']!=\"Yes\":\n return None,msg\n\ntry:\n r = OpenScanCloud('getTokenInfo', {'token':msg['token']})\n if r.status_code != 200:\n msg['payload'] = 'Could not verify token'\n return msg \n \n msg1 = r.json()\n \n save('osc_credit',msg1['credit'])\n save('osc_limit_filesize',msg1['limit_filesize'])\n save('osc_limit_photos',msg1['limit_photos'])\n msg1['enabled'] = True\nexcept:\n pass\n\nsave('token',msg['token'])\n \nmsg['payload'] = 'Token verified and saved'\nreturn msg, msg1", "outputs": 2, "x": 750, "y": 1320, "wires": [ [ "76acd48a511a5e3e", "b01581296b94dfcd" ], [ "9c51aa678f16980f" ] ] }, { "id": "76acd48a511a5e3e", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "topic": "", "name": "", "x": 890, "y": 1280, "wires": [ [] ] }, { "id": "5f50ed3f6ba37cef", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "", "label": "Token", "tooltip": "", "group": "3b4bd36726be16d5", "order": 2, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "", "sendOnBlur": true, "className": "", "topicType": "str", "x": 350, "y": 1340, "wires": [ [ "cb62d30728af2968" ] ] }, { "id": "cb62d30728af2968", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Save?", "func": "msg['token'] = msg['payload']\n\nif len(msg['payload'])>=14:\n \n msg[\"payload\"]='Save and verify token: ' + msg['payload']\n return msg\nelse:\n return None,msg", "outputs": 2, "x": 470, "y": 1340, "wires": [ [ "94e503dd2e64d903" ], [ "d859bb39914d4999" ] ] }, { "id": "0dd01eef6e70059e", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "text", "func": "from OpenScan import load_str\n\ntoken = load_str('token')[0:8]\nmsg['payload']= token + '...'\nif len(token)==0:\n msg['payload']=\"enter token\"\nreturn msg", "outputs": 1, "x": 230, "y": 1340, "wires": [ [ "5f50ed3f6ba37cef" ] ] }, { "id": "788fabff98c7973c", "type": "link in", "z": "017bd4e4a428bee5", "name": "token", "links": [ "960912e90ba5b5bc", "b01581296b94dfcd", "d859bb39914d4999" ], "x": 75, "y": 1340, "wires": [ [ "0dd01eef6e70059e" ] ] }, { "id": "94e503dd2e64d903", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 610, "y": 1320, "wires": [ [ "88e92b621d2a3394" ] ] }, { "id": "d859bb39914d4999", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "788fabff98c7973c" ], "x": 555, "y": 1360, "wires": [] }, { "id": "9c51aa678f16980f", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "links": [ "5d267acc10020091", "397ab7f44b893c89" ], "x": 835, "y": 1360, "wires": [] }, { "id": "397ab7f44b893c89", "type": "link in", "z": "017bd4e4a428bee5", "name": "OSCparameters", "links": [ "960912e90ba5b5bc", "9c51aa678f16980f", "b42e061fb1f1f3d7" ], "x": 75, "y": 1380, "wires": [ [ "a7fd00943edc380b" ] ] }, { "id": "b01581296b94dfcd", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "links": [ "788fabff98c7973c", "5d267acc10020091" ], "x": 835, "y": 1320, "wires": [] }, { "id": "bf6d941ad307ce22", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 250, "y": 1520, "wires": [ [ "f22dfef37d5de773" ] ] }, { "id": "f22dfef37d5de773", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "import re\n\nif msg['payload'] == 'Cancel':\n return\n\nmail = msg['payload']\nemail_regex = re.compile(r\"[^@]+@[^@]+\\.[^@]+\")\n\nif email_regex.match(mail) != None:\n msg['mail'] = mail\n msg['topic'] = 'OpenScanCloud Registration (2/3)'\n msg['payload'] = 'Enter your first name'\n return msg\nmsg['payload'] = 'invalid input'\nreturn None,msg\n", "outputs": 2, "x": 390, "y": 1520, "wires": [ [ "54602ee49ca022e7" ], [ "1505f3e72f971081" ] ] }, { "id": "1505f3e72f971081", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 530, "y": 1560, "wires": [ [] ] }, { "id": "54602ee49ca022e7", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 530, "y": 1520, "wires": [ [ "f9efcb87b74abbd4" ] ] }, { "id": "510dbe4d76253bd6", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "SUBMIT", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 810, "y": 1520, "wires": [ [ "600b2306caed1640" ] ] }, { "id": "600b2306caed1640", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "import requests\nimport os\nfrom OpenScan import OpenScanCloud\n\nif msg['payload'] == 'Cancel':\n return\n\nmsg['lastname'] = msg['payload']\n\nmsg2 = {}\n\nfor i in ['forename','lastname','mail']:\n msg2[i] = msg[i]\n\nr = OpenScanCloud('requestToken',msg2)\n\nstatus = r.status_code\n\nmsg['topic'] = 'OpenScanCloud Registration - Success'\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\n\nif status != 200:\n msg['topic'] = 'OpenScanCloud Registration - Failed'\n msg['payload'] = 'Registration failed, please try again.'\n\nmsg['status'] = status\n\nreturn msg", "outputs": 1, "x": 950, "y": 1520, "wires": [ [ "bbad1ab5f8f63fb7" ] ] }, { "id": "d34cd203725bac15", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Register", "group": "3b4bd36726be16d5", "order": 7, "width": 2, "height": 1, "passthru": false, "label": "Register", "tooltip": "testtesttest", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "Please enter your email address:", "payloadType": "str", "topic": "Requesting an OpenScanCloud Token", "topicType": "str", "x": 100, "y": 1520, "wires": [ [ "bf6d941ad307ce22" ] ] }, { "id": "bbad1ab5f8f63fb7", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 1090, "y": 1520, "wires": [ [] ] }, { "id": "a7fd00943edc380b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "from OpenScan import load_int\n\nmsg = {}\n\ntry:\n msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\n msg['limit_filesize'] = float(int(load_int('osc_limit_filesize')/10000000))/100\n msg['limit_photos'] = load_int('osc_limit_photos')\n return msg\nexcept:\n pass", "outputs": 1, "x": 230, "y": 1380, "wires": [ [ "f7bf47e3eec6d736", "b52d91c628b151a4", "1969c709ef2fd1d5" ] ] }, { "id": "124459147143ec6a", "type": "comment", "z": "017bd4e4a428bee5", "name": "Motor", "info": "", "x": 90, "y": 1600, "wires": [] }, { "id": "dbd62b91a6c9c412", "type": "comment", "z": "017bd4e4a428bee5", "name": "Camera", "info": "", "x": 90, "y": 2240, "wires": [] }, { "id": "842b6fe016087ce3", "type": "comment", "z": "017bd4e4a428bee5", "name": "Pinout", "info": "", "x": 110, "y": 2860, "wires": [] }, { "id": "8c1a92f2dcc976c7", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "Rotor_delay (ms)", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 14, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "0.2", "step": "0.005", "className": "", "x": 450, "y": 1840, "wires": [ [ "bb54bbdae6690576" ] ] }, { "id": "2647111c06f2055d", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt delay", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 27, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "0.2", "step": "0.005", "className": "", "x": 420, "y": 2080, "wires": [ [ "fb8145a9f8d4f7b2" ] ] }, { "id": "f9b51424edb0491c", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_acc", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 16, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.1", "max": "2", "step": "0.1", "className": "", "x": 420, "y": 1880, "wires": [ [ "ea87ecfd2af3cc7f" ] ] }, { "id": "1ab34b0a78b2c577", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_accramp", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 18, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "100", "max": "5000", "step": "100", "className": "", "x": 440, "y": 1920, "wires": [ [ "249f44c3a87793ba" ] ] }, { "id": "1d4230b3d9b93f63", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotor_stepsperrotation", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 12, "width": 3, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 460, "y": 1800, "wires": [ [ "0bb56b1edb12c2cf" ] ] }, { "id": "2e3222f0aba88040", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 17, "width": 3, "height": 1, "name": "rotor Accramp", "label": "Acceleration ramp", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1880, "wires": [] }, { "id": "9d50311679acf215", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 11, "width": 3, "height": 1, "name": "rotor_Steps per Rotation", "label": "Steps per Rotation", "format": "", "layout": "row-spread", "className": "", "x": 810, "y": 1920, "wires": [] }, { "id": "25d7b4dd2aab8f05", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 15, "width": 3, "height": 1, "name": "rotor Acc", "label": "Acceleration", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 1840, "wires": [] }, { "id": "15682cca9622831f", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 13, "width": 3, "height": 1, "name": "rotor_delay", "label": "Delay", "format": "", "layout": "row-left", "className": "", "x": 770, "y": 1800, "wires": [] }, { "id": "8e2d22042bfcb4e8", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 23, "width": 6, "height": 1, "name": "tt", "label": "TURNTABLE", "format": "", "layout": "row-center", "className": "", "x": 90, "y": 2040, "wires": [] }, { "id": "56bc3b93af2ebe16", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_acc", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 29, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.1", "max": "2", "step": "0.1", "className": "", "x": 410, "y": 2120, "wires": [ [ "35422077b53da9bf" ] ] }, { "id": "6ef996f8a36f94c2", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_accramp", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 31, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "500", "step": "1", "className": "", "x": 430, "y": 2160, "wires": [ [ "2c000bd53cdb98ca" ] ] }, { "id": "0c50fdbb5ac3c373", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "tt_stepsperrotation", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 25, "width": 3, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 450, "y": 2040, "wires": [ [ "485a4bed5a6bea23" ] ] }, { "id": "213ccfb441a42890", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 30, "width": 3, "height": 1, "name": "ttAccramp", "label": "Acceleration ramp", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 2160, "wires": [] }, { "id": "73c9b4d09dc25e54", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 24, "width": 3, "height": 1, "name": "tt_steps per Rotation", "label": "Steps per Rotation", "format": "", "layout": "row-spread", "className": "", "x": 800, "y": 2040, "wires": [] }, { "id": "a81824c92f22487d", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 28, "width": 3, "height": 1, "name": "tt Acc", "label": "Acceleration", "format": "", "layout": "row-left", "className": "", "x": 750, "y": 2120, "wires": [] }, { "id": "9715161858f69649", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 26, "width": 3, "height": 1, "name": "tt_delay", "label": "Delay", "format": "", "layout": "row-left", "className": "", "x": 760, "y": 2080, "wires": [] }, { "id": "1b3ac50d2c6600c6", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_angle", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 20, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "180", "step": "1", "className": "", "x": 430, "y": 1960, "wires": [ [ "e0d7c36daa42b3f3" ] ] }, { "id": "6dcd1f0ccb01a299", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 19, "width": 3, "height": 1, "name": "rotor_angle", "label": "Manual angle", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 1960, "wires": [] }, { "id": "16e9a3a71c4bb916", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_angle", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 33, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "180", "step": "1", "className": "", "x": 420, "y": 2200, "wires": [ [ "c34111aaec734dd9" ] ] }, { "id": "888161059eb9c71c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 32, "width": 3, "height": 1, "name": "tt_angle", "label": "Manual angle", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2200, "wires": [] }, { "id": "f4fc72297074c7ae", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 4, "width": 6, "height": 1, "name": "rotor", "label": "ROTOR", "format": "", "layout": "row-center", "className": "", "x": 90, "y": 1680, "wires": [] }, { "id": "9b1d8f9e21b34102", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "tt_dir", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 35, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "1", "className": "", "x": 410, "y": 2240, "wires": [ [ "89dbbe7d99ddbbaf" ] ] }, { "id": "b2e839fe47a32b5f", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_dir", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 22, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "1", "step": "1", "className": "", "x": 420, "y": 2000, "wires": [ [ "204b0a5c8629d78a" ] ] }, { "id": "4519daf0b4b28aef", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 34, "width": 3, "height": 1, "name": "tt_dir", "label": "Direction", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2240, "wires": [] }, { "id": "5f269ea2c8a53f6c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 21, "width": 3, "height": 1, "name": "rotor_dir", "label": "Direction", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2000, "wires": [] }, { "id": "b67dfacfc9a23aa5", "type": "link in", "z": "017bd4e4a428bee5", "name": "advanced settings", "links": [ "62cd775a1c02dac8" ], "x": 95, "y": 80, "wires": [ [ "9b756a1f9b0e7317" ] ] }, { "id": "62cd775a1c02dac8", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "b67dfacfc9a23aa5" ], "x": 955, "y": 660, "wires": [] }, { "id": "9d94dbc523d989a3", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_delay_after", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 16, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 450, "y": 2460, "wires": [ [ "b81e238ccd0a04fe" ] ] }, { "id": "0558d6eb9a01862e", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_delay_before", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 14, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 440, "y": 2500, "wires": [ [ "a0048747e7300bdc" ] ] }, { "id": "d47515c9b208bfb7", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_timeout", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 12, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.01", "max": "1", "step": "0.01", "className": "", "x": 420, "y": 2420, "wires": [ [ "9b0d5c521a7822cc" ] ] }, { "id": "89c76766c7552b57", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_gain", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 22, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "10", "step": "0.1", "className": "", "x": 410, "y": 2540, "wires": [ [ "9b26ed02296d27c9" ] ] }, { "id": "c385518eb65a1b27", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_awbg_red", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 18, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-10", "max": "10", "step": "0.1", "className": "", "x": 430, "y": 2580, "wires": [ [ "b0ac7e9a7c713b84" ] ] }, { "id": "5c80833b718d9bf6", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_awbg_blue", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 20, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-10", "max": "10", "step": "0.1", "className": "", "x": 430, "y": 2620, "wires": [ [ "827b1a671a77037d" ] ] }, { "id": "5a3826e112fb24e6", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_contrast", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 24, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 430, "y": 2660, "wires": [ [ "78a1536c167da741" ] ] }, { "id": "3182ed7ac02b1509", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_saturation", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 26, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "5", "step": "0.1", "className": "", "x": 430, "y": 2700, "wires": [ [ "fe9a5b68fc8c2077" ] ] }, { "id": "7fa6337cdf0a0bc8", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_jpeg_q", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 3, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "100", "step": "1", "className": "", "x": 420, "y": 2740, "wires": [ [ "e27d2613e942f344" ] ] }, { "id": "08275bf96f87b8ef", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 11, "width": 3, "height": 1, "name": "timeout", "label": "Timeout", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2420, "wires": [] }, { "id": "d2d028df4a139f41", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 15, "width": 3, "height": 1, "name": "delay_after", "label": "Delay after", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2460, "wires": [] }, { "id": "c6a65762aa4ffb7b", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 13, "width": 3, "height": 1, "name": "delay_before", "label": "Delay before", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2500, "wires": [] }, { "id": "780323fd4504b855", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 21, "width": 3, "height": 1, "name": "gain", "label": "Gain", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2540, "wires": [] }, { "id": "780bf08b41202135", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 17, "width": 3, "height": 1, "name": "awbg red", "label": "AWBG red", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2580, "wires": [] }, { "id": "c0faf441fc918538", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 19, "width": 3, "height": 1, "name": "awbg blue", "label": "AWBG blue", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2620, "wires": [] }, { "id": "93d12b447a39c5bb", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 23, "width": 3, "height": 1, "name": "contrast", "label": "Contrast", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2660, "wires": [] }, { "id": "e77e6dcd285d3062", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 25, "width": 3, "height": 1, "name": "saturation", "label": "Saturation", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2700, "wires": [] }, { "id": "a7075bc8d5ee1138", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 2, "width": 3, "height": 1, "name": "jpegQ", "label": "Jpeg Quality", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2740, "wires": [] }, { "id": "282681e7c4351f74", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "ext", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 3, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 2900, "wires": [ [ "b17e82651407d8e0" ] ] }, { "id": "da43c58979737fec", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 2, "width": 4, "height": 1, "name": "ext", "label": "External Camera", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2900, "wires": [] }, { "id": "ef70d61678fe1f11", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "light1", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 5, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 2940, "wires": [ [ "2c812acffdb330c5" ] ] }, { "id": "fec56a7e913b21d6", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 4, "width": 4, "height": 1, "name": "light1", "label": "Light 1", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2940, "wires": [] }, { "id": "24929b4629f22070", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "light2", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 7, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 2980, "wires": [ [ "ae0654af69446942" ] ] }, { "id": "7c6bdc0504aa4cc7", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 6, "width": 4, "height": 1, "name": "light2", "label": "Light 2", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 2980, "wires": [] }, { "id": "8c396b060f3d2646", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotordir", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 9, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3020, "wires": [ [ "58cf48cfacc979fb" ] ] }, { "id": "97568610daccf74a", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 8, "width": 4, "height": 1, "name": "rotordir", "label": "Rotor direction", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3020, "wires": [] }, { "id": "a3c58ea48c388215", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotorstep", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 11, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3060, "wires": [ [ "c7ae206f2fff6810" ] ] }, { "id": "6da92aeaeffd95e0", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 10, "width": 4, "height": 1, "name": "rotorstep", "label": "Rotor step", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3060, "wires": [] }, { "id": "9b5da90eaf6ac562", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "rotoren", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 13, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3100, "wires": [ [ "cfebd4a47a68b319" ] ] }, { "id": "12623e4addfa2c22", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 12, "width": 4, "height": 1, "name": "rotoren", "label": "Rotor enable", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3100, "wires": [] }, { "id": "f24cb404d7d09f8a", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "ttdir", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 15, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 3140, "wires": [ [ "90f4d220928e4727" ] ] }, { "id": "542bfb9d92935c2c", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 14, "width": 4, "height": 1, "name": "ttdir", "label": "Turntable direction", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 3140, "wires": [] }, { "id": "1f79467df98ce894", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "ttstep", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 17, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 3180, "wires": [ [ "b05e1e612887f9c2" ] ] }, { "id": "170d3b925f7745cc", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 16, "width": 4, "height": 1, "name": "ttstep", "label": "Turntable step", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 3180, "wires": [] }, { "id": "65b0130e390c2e67", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "tten", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 19, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 410, "y": 3220, "wires": [ [ "fe22723ce5a3495f" ] ] }, { "id": "10ac340984418a58", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 18, "width": 4, "height": 1, "name": "tten", "label": "Turntable enable", "format": "", "layout": "row-spread", "className": "", "x": 750, "y": 3220, "wires": [] }, { "id": "661614f5bd2c71d6", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "endstop1", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 21, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3260, "wires": [ [ "2af447a6905b83bc" ] ] }, { "id": "c18b55859dae5f85", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 20, "width": 4, "height": 1, "name": "endstop1", "label": "Endstop 1", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3260, "wires": [] }, { "id": "e23a396162026618", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "endstop2", "label": "", "tooltip": "", "group": "644b3bcc903d46ca", "order": 23, "width": 2, "height": 1, "passthru": false, "mode": "number", "delay": "0", "topic": "topic", "sendOnBlur": true, "className": "", "topicType": "msg", "x": 420, "y": 3300, "wires": [ [ "787a128f84f747c0" ] ] }, { "id": "82c1a33014d003e9", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "644b3bcc903d46ca", "order": 22, "width": 4, "height": 1, "name": "endstop1", "label": "Endstop 2", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 3300, "wires": [] }, { "id": "5255759a7c5b2a74", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "confirm", "x": 680, "y": 660, "wires": [ [ "15fd1c9e5610cb85" ] ] }, { "id": "8be8015931c663cc", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "from OpenScan import save, load_bool\n\nif msg['payload'] == True and not load_bool('advanced_settings'):\n msg['payload'] = '''

PLEASE READ :)

\n

Modifying the advanced settings can potentially damage your device and/or the connected peripherals.

\n

Please read the given information texts carefully and only change settings, when you are sure about the consequences!

\n'''\n return msg\nelif not msg['payload']: \n save('advanced_settings', False)\n", "outputs": 1, "x": 530, "y": 660, "wires": [ [ "5255759a7c5b2a74" ] ] }, { "id": "9d464b2ba1edaf48", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "cam_rotation", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 10, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "270", "step": "90", "className": "", "x": 420, "y": 2780, "wires": [ [ "b7d3fe0c0b40b3e1" ] ] }, { "id": "db98b95693ebce63", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 9, "width": 3, "height": 1, "name": "cam_rot", "label": "Image Rotation", "format": "", "layout": "row-spread", "className": "", "x": 760, "y": 2780, "wires": [] }, { "id": "6659121906897a1f", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1800, "wires": [ [ "1d4230b3d9b93f63" ] ] }, { "id": "0bb56b1edb12c2cf", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1800, "wires": [ [] ] }, { "id": "569829eeff715c33", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1920, "wires": [ [ "1ab34b0a78b2c577" ] ] }, { "id": "249f44c3a87793ba", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1920, "wires": [ [] ] }, { "id": "c997e60519341afd", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1960, "wires": [ [ "1b3ac50d2c6600c6" ] ] }, { "id": "e0d7c36daa42b3f3", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1960, "wires": [ [] ] }, { "id": "59ecf3a22cd3a669", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2000, "wires": [ [ "b2e839fe47a32b5f" ] ] }, { "id": "204b0a5c8629d78a", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nif (msg.payload === 1){\n content = '1'\n}\nelse{\n content = '-1'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2000, "wires": [ [] ] }, { "id": "15f02421b30a9ab6", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'rotor_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) * 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1840, "wires": [ [ "8c1a92f2dcc976c7" ] ] }, { "id": "bb54bbdae6690576", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload / 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1840, "wires": [ [] ] }, { "id": "58928befcc61b1f7", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'rotor_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1880, "wires": [ [ "f9b51424edb0491c" ] ] }, { "id": "ea87ecfd2af3cc7f", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1880, "wires": [ [] ] }, { "id": "27bc56f273360ac7", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nsteps = parseInt(data);\nif (steps == 3600){\n steps = 3200\n}\n\nmsg.payload = steps\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2040, "wires": [ [ "0c50fdbb5ac3c373" ] ] }, { "id": "f46ced86106306c8", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2160, "wires": [ [ "6ef996f8a36f94c2" ] ] }, { "id": "4339704cd8552eb3", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2200, "wires": [ [ "16e9a3a71c4bb916" ] ] }, { "id": "1ac53bb6150645fe", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2240, "wires": [ [ "9b1d8f9e21b34102" ] ] }, { "id": "9b89eb1eaf333c10", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'tt_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) * 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2080, "wires": [ [ "2647111c06f2055d" ] ] }, { "id": "2e8927be0e235fa1", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'tt_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2120, "wires": [ [ "56bc3b93af2ebe16" ] ] }, { "id": "485a4bed5a6bea23", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_stepsperrotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2040, "wires": [ [] ] }, { "id": "2c000bd53cdb98ca", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_accramp'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2160, "wires": [ [] ] }, { "id": "c34111aaec734dd9", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_angle'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2200, "wires": [ [] ] }, { "id": "89dbbe7d99ddbbaf", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nif (msg.payload === 1){\n content = '1'\n}\nelse{\n content = '-1'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2240, "wires": [ [] ] }, { "id": "fb8145a9f8d4f7b2", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_delay'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload / 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2080, "wires": [ [] ] }, { "id": "35422077b53da9bf", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'tt_acc'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2120, "wires": [ [] ] }, { "id": "d5308090f2b7971a", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_timeout'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) / 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2420, "wires": [ [ "d47515c9b208bfb7" ] ] }, { "id": "9b0d5c521a7822cc", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_timeout'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload * 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2420, "wires": [ [] ] }, { "id": "694d1068bea15171", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_delay_after'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2460, "wires": [ [ "9d94dbc523d989a3" ] ] }, { "id": "cec3e5e78a40476b", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_delay_before'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2500, "wires": [ [ "0558d6eb9a01862e" ] ] }, { "id": "b81e238ccd0a04fe", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_delay_after'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2460, "wires": [ [] ] }, { "id": "a0048747e7300bdc", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_delay_before'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2500, "wires": [ [] ] }, { "id": "6f524f9370a18482", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_gain'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2540, "wires": [ [ "89c76766c7552b57" ] ] }, { "id": "9b26ed02296d27c9", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_gain'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2540, "wires": [ [] ] }, { "id": "1f87f473e327c3cc", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_awbg_red'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2580, "wires": [ [ "c385518eb65a1b27" ] ] }, { "id": "b0ac7e9a7c713b84", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_awbg_red'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2580, "wires": [ [] ] }, { "id": "cff7ac5f1e061855", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_awbg_blue'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2620, "wires": [ [ "5c80833b718d9bf6" ] ] }, { "id": "827b1a671a77037d", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_awbg_blue'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2620, "wires": [ [] ] }, { "id": "cf854461c37ca54f", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_contrast'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2660, "wires": [ [ "5a3826e112fb24e6" ] ] }, { "id": "78a1536c167da741", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_contrast'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2660, "wires": [ [] ] }, { "id": "ba10e04dd1761692", "type": "function", "z": "017bd4e4a428bee5", "name": "loadF", "func": "var file = 'cam_saturation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2700, "wires": [ [ "3182ed7ac02b1509" ] ] }, { "id": "fe9a5b68fc8c2077", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_saturation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2700, "wires": [ [] ] }, { "id": "a69d216114f908a5", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_jpeg_quality'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2740, "wires": [ [ "7fa6337cdf0a0bc8" ] ] }, { "id": "e27d2613e942f344", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_jpeg_quality'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2740, "wires": [ [] ] }, { "id": "f02d4a036a225e87", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_rotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2780, "wires": [ [ "9d464b2ba1edaf48" ] ] }, { "id": "b7d3fe0c0b40b3e1", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_rotation'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2780, "wires": [ [] ] }, { "id": "612cccacda1a65aa", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_external'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2900, "wires": [ [ "282681e7c4351f74" ] ] }, { "id": "b17e82651407d8e0", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_external'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 2900, "wires": [ [] ] }, { "id": "3b126549c03a872e", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_endstop1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3260, "wires": [ [ "661614f5bd2c71d6" ] ] }, { "id": "2af447a6905b83bc", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_endstop1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3260, "wires": [ [] ] }, { "id": "954db931f87894ee", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_ringlight1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2940, "wires": [ [ "ef70d61678fe1f11" ] ] }, { "id": "2c812acffdb330c5", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_ringlight1'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 2940, "wires": [ [] ] }, { "id": "6682c8057e89d087", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_ringlight2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2980, "wires": [ [ "24929b4629f22070" ] ] }, { "id": "ae0654af69446942", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_ringlight2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 2980, "wires": [ [] ] }, { "id": "015be401d08047d2", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3020, "wires": [ [ "8c396b060f3d2646" ] ] }, { "id": "58cf48cfacc979fb", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_rotor_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3020, "wires": [ [] ] }, { "id": "1c6c0f8b9ac95659", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_rotor_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3060, "wires": [ [ "a3c58ea48c388215" ] ] }, { "id": "c7ae206f2fff6810", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_rotor_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3060, "wires": [ [] ] }, { "id": "dcee66c0d56c6934", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_rotor_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3100, "wires": [ [ "9b5da90eaf6ac562" ] ] }, { "id": "cfebd4a47a68b319", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_rotor_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3100, "wires": [ [] ] }, { "id": "6ec7d85bb17eb159", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3140, "wires": [ [ "f24cb404d7d09f8a" ] ] }, { "id": "4f42d02a3776a006", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_tt_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3180, "wires": [ [ "1f79467df98ce894" ] ] }, { "id": "5d70f4715c9a5ae1", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_tt_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3220, "wires": [ [ "65b0130e390c2e67" ] ] }, { "id": "90f4d220928e4727", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_tt_dir'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3140, "wires": [ [] ] }, { "id": "b05e1e612887f9c2", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_tt_step'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3180, "wires": [ [] ] }, { "id": "fe22723ce5a3495f", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_tt_enable'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3220, "wires": [ [] ] }, { "id": "58bbe9fc41e0d7b9", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'pin_endstop2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 3300, "wires": [ [ "e23a396162026618" ] ] }, { "id": "787a128f84f747c0", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'pin_endstop2'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 610, "y": 3300, "wires": [ [] ] }, { "id": "78351089ee9ebeaf", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'ssh'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 340, "wires": [ [ "40dee936a9abac0d" ] ] }, { "id": "5fba78ae65eaaf5d", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'smb'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 380, "wires": [ [ "4fd9bb53fdb51a25" ] ] }, { "id": "67206663b3881868", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'advanced_settings'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 660, "wires": [ [ "c833f6243a059d83" ] ] }, { "id": "3492754252645e62", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'camera'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data.replace(/(\\r\\n|\\n|\\r)/gm,\"\"));\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 420, "wires": [ [ "a2c1dba3e67be015", "6f3d403e157163e4" ] ] }, { "id": "d16525a31223bc42", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'model'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 620, "wires": [ [ "80b579a4220e5c23", "c6138801b30f091d" ] ] }, { "id": "f99ec8781a33ec7d", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.payload = 'This is a free piece of software and it is provided as is, without any warranty.
There might be functions that need a connection to the internet: '+\n '

By pressing GET FEATURES you agree that the shown preview image will be transfered, stored and processed via SFTP to my servers '+\n '(Thomas Megel, OpenScan, Halle, Germany). The IP address will be saved for 14 days The images might be used for further experiments (e.g. machine learning, automation ...). '+\n '

By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\n 'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\n 'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\n '

By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\n 'The raw images and resulting 3d models will never be published without your explicit consent.'+ \n '

If you have any questions you can contact me at info@openscan.eu.'+ \n '

THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\n 'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \n 'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \n 'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 300, "wires": [ [ "7dc39bd847d16ded" ] ] }, { "id": "5f849178998d9082", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "if(msg.payload === 'Agree'){\n data = true;\n}\nelse{\n data = false;\n}\nvar file = 'terms'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nfs.writeFile(filepath+file, String(data), err => {\n if (err) {\n return msg\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 550, "y": 300, "wires": [ [] ] }, { "id": "725fd0cab0bddc0e", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'hostname'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 940, "wires": [ [ "49259adad52fc214" ] ] }, { "id": "49259adad52fc214", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "", "label": "Hostname", "tooltip": "", "group": "0fe66c9190b8a87c", "order": 6, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "Change hostname to:", "sendOnBlur": true, "className": "", "topicType": "str", "x": 530, "y": 940, "wires": [ [ "8001f7c361de7d8c" ] ] }, { "id": "51521bc6eb44cde5", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 250, "y": 980, "wires": [ [ "59c9f67283ba1709" ] ] }, { "id": "2bb52656f9554dab", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "ssid = msg.payload\nmsg.topic = 'Add wifi network (' + ssid + ')'\nmsg.payload = 'Enter Wifi password:'\nmsg.ssid = ssid\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 650, "y": 980, "wires": [ [ "ebcc98685059b9d4" ] ] }, { "id": "ebce67b739d1891f", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "chk/change hostname", "func": "from OpenScan import save\n\nif msg['payload'] != 'OK':\n pass\n\nwith open('/etc/hostname', 'r') as file:\n old_hostname = file.read().replace('\\n','')\n\nhostname = msg['hostname']\nif len(hostname) < 4 :\n msg['payload'] = ' '\n msg['topic'] = 'ERROR - Hostname NOT changed'\n return msg\n \n\nwith open('/etc/hostname', 'w+') as file:\n file.write(hostname)\nos.system('echo ' + hostname + ' | tee /etc/hostname')\nwith open('/etc/hosts', 'r') as file:\n temp = file.read()\ntemp = temp.replace(old_hostname,hostname)\nwith open('/etc/hosts', 'w') as file:\n file.write(temp)\nos.system('hostnamectl set-hostname ' + hostname)\nos.system('systemctl restart avahi-daemon')\nsave('hostname',hostname)\nmsg['payload'] = hostname\nmsg['topic'] = 'Success - Hostname changed'\nreturn msg\n", "outputs": 1, "x": 1140, "y": 940, "wires": [ [ "03732a7d3b0c95aa" ] ] }, { "id": "667ac2aba819f506", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "Confirm", "x": 920, "y": 940, "wires": [ [ "ebce67b739d1891f" ] ] }, { "id": "8001f7c361de7d8c", "type": "change", "z": "017bd4e4a428bee5", "name": "", "rules": [ { "t": "set", "p": "hostname", "pt": "msg", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 710, "y": 940, "wires": [ [ "667ac2aba819f506" ] ] }, { "id": "9bb0adbd716ce347", "type": "link in", "z": "017bd4e4a428bee5", "name": "reboot", "links": [ "16c76929f88df841", "fe3a855fee9e28c6" ], "x": 155, "y": 720, "wires": [ [ "d114f4d4d7f31981", "cc3cb10f2ea3f8b8" ] ] }, { "id": "f9efcb87b74abbd4", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "if (msg.payload === 'Cancel'){\n return\n}\nmsg.forename = msg.payload\nmsg.topic = 'OpenScanCloud Registration (3/3)'\nmsg.payload = 'Enter your last name'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 670, "y": 1520, "wires": [ [ "510dbe4d76253bd6" ] ] }, { "id": "adc206aa8edd1e41", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "OSC", "group": "db43d646.2074c8", "order": 2, "width": 3, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Files&Cloud

Refresh

You can refresh the status of the processing of your files in the OpenScanCloud. Make sure to read and agree the terms of use (in settings menu) before using the OpenScanCloud. Do not spam this button, as this might lead to temporary/permanent suspension of your IP address.

The status (in the table) of the individual sets in the file list will be updated to one of the following:

Created - you started the upload of your image set. If you are stuck on this status, please try to restart the upload.

Initialized - all files have been uploaded and processing will start as soon as possible

File approved - the server received and verified your files

Processing started - your files are currently being processed

Processing failed - there are various reasons why processing might fail. Please check the email for more details or contact me at cloud@openscan.eu

processing done - check your email, where you should find a link to the 3d model :)

Status (on the right column)

Indicates, what the device is currently up to.

Refreshing - updating all image set's status

Uploading - while transferring the image set to the OpenScanCloud servers. If the upload freezes, be patient. If nothing happens, reboot the device and restart the upload.

Project started - when the upload of a set was successful

Zipping - files larger then 200mb have to be split and re-zipped before uploading to the OpenScanCloud, the process might take a while depending on the filesize.

Combining - two sets into one might take up to a minute.

Set

select a set from the file list by clicking on a row in the table

Download

Download the selected set from the OpenScan device to your computer/mobile/tablet

Upload

Upload the selected file to the OpenScanCloud

Combine

In order to combine two sets, select one set. Click the combine button and select the second set. A pop-up will appear, and you can confirm the operation. All images from the two sets will be merged into one set. The original image sets will be deleted!

Delete Set/All

Please keep in mind, that the memory of the SD card is relatively small, and thus you will have to delete individual or all photo sets from time to time.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 590, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "45df91cae421e8e1", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Scan_settings", "group": "7aaf184330605300", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Scan Settings

Current Status

--READY-- - everything is okay and ready to go :)

Routine-preparing - before starting the routine some time might pass depending on the number of photos

Routine-stopping - manually ending the routine by pressing the stop button

Routine-Photo X/Y - Showing the progress of the routine

No Camera Found - please check the camera ribbon cable

Error: XXX - Please contact info@openscan.eu or post an issue on Github.com

Projectname

Each photo set will be saved using the following pattern  YYYY-MM-DD_hh-mm-ss_projectname.zip (e.g. 2022-04-05_12.12.12_toysoldier.zip). Keep your files organized by giving each set a new projectname. If not specified 'default' will be used.

Rotor

Moving the rotor by increments of 5°. Please make sure to start the routine with the camera in the horizontal position.

Turntable

Moving the turntable by increments of 15°.

Ringlight

Use the ring light for shadow-free illumination. It is highly recommended to use the polarizer in order to avoid reflections. Note, that the polarizer will absorb 75% of the light, so you might need to use both ring lights.

Photos

Set the number of photos for the current set. 60-120 photos should be more than enough for most objects. If the reconstruction fails or is very bad with 60 photos, increasing the number of photos will not help!

Shutter

Again: Less is more! If the value is too high, some areas might get overexposed and thus, the software will not be able to recognize the surface feature of the object. Here are some reference values:

- no polarizer: 5-20ms

- mostly white object,  with polarizer + one ringlight: 50-200ms

Crop X/Y

Make sure to use the right object holder to place the object in the middle of the screen. Try to crop as many unnecessary areas as possible. This will greatly lower the file size and resulting transfer and reconstruction times!

Start/Stop

Use the buttons to start/stop the routine

Reboot/Shutdown

In case of an error, try to restart the device. Always use the shutdown button before powering-off the device!

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 760, "y": 120, "wires": [ [ "f304680180a23479" ] ] }, { "id": "e9677b85856b5873", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Reset rfkill", "func": "from os import system\nif \"Interface doesn't support scanning\" in msg['payload']:\n system('rfkill unblock all')\n system('ifconfig wlan0 up')\n return msg", "outputs": 1, "x": 390, "y": 1100, "wires": [ [] ] }, { "id": "91fe20cb16f54293", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_anglemin'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1680, "wires": [ [ "327c8bdde31033a4" ] ] }, { "id": "add3e998b097c54f", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 7, "width": 3, "height": 1, "name": "rotor_anglemin", "label": "Min Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1680, "wires": [] }, { "id": "da286366433c83a0", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_anglemin'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1680, "wires": [ [] ] }, { "id": "327c8bdde31033a4", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_anglemin", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 8, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1680, "wires": [ [ "da286366433c83a0" ] ] }, { "id": "94288df4c6756197", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_anglemax", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 10, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1720, "wires": [ [ "e531ffe3dcf34eb4" ] ] }, { "id": "4702a4a09124e27d", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "rotor_anglestart", "label": "", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 6, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "-90", "max": "90", "step": "5", "className": "", "x": 440, "y": 1760, "wires": [ [ "9ce407cb16f0419a" ] ] }, { "id": "2cf946c7aab2cbb4", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_anglemax'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1720, "wires": [ [ "94288df4c6756197" ] ] }, { "id": "e531ffe3dcf34eb4", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_anglemax'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1720, "wires": [ [] ] }, { "id": "4da5f650d3845baa", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'rotor_anglestart'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1760, "wires": [ [ "4702a4a09124e27d" ] ] }, { "id": "9ce407cb16f0419a", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'rotor_anglestart'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1760, "wires": [ [] ] }, { "id": "fda776c5aa642867", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 9, "width": 3, "height": 1, "name": "rotor_anglemax", "label": "Max Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1720, "wires": [] }, { "id": "6e9af48a1c4c58c6", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "d49a6dfd7fb17096", "order": 5, "width": 3, "height": 1, "name": "rotor_anglestart", "label": "Start Angle", "format": "", "layout": "row-left", "className": "", "x": 780, "y": 1760, "wires": [] }, { "id": "9b2bc9849aee310b", "type": "link in", "z": "017bd4e4a428bee5", "name": "changeHostname", "links": [ "ec2db55a99bbe3ee", "d5175561293ef490", "960912e90ba5b5bc" ], "x": 835, "y": 900, "wires": [ [ "8b9e3781511e9231" ] ] }, { "id": "8b9e3781511e9231", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "chk", "func": "with open('/etc/hostname', 'r') as file:\n old_hostname = file.read().replace('\\n','')\nif old_hostname == 'raspberrypi':\n msg['hostname'] = 'openscan'\n msg['payload'] = 'OK'\n return msg", "outputs": 1, "x": 930, "y": 900, "wires": [ [ "ebce67b739d1891f" ] ] }, { "id": "3fcbd9fe3acc3fb7", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "scan_arducam", "group": "90223f7ddc082321", "order": 1, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Focus Settings

MF - Manual Focus

By default, the switch is 'off', which means that autofocus is active. For small objects, it might be necessary to use manual focus: activate the switch and set the focus by pressing + and - accordingly. The distance is measured between the camera lens and the focal plane (which should be in the center or slightly in front of the center of the object). Be aware, that the distance value is only a rough estimate (mm)

ST - Stacking

Stacking is disabled by default. Once activated, you will be able to set the following:

Stacksize - defines the number of photos between the minimal and the maximal focal distance

SET press this button to set the maximal/minimal focal distance. Pressing the button a third time will re-set the values.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 760, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "6d68cccec646e0a0", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "enable routine", "func": "msg_enable = {}\nmsg_disable = {}\n\nmsg_enable['enabled'] = True\nmsg_disable['enabled'] = False\n\nif msg['payload'] == 'external':\n return msg_enable, msg_disable\nif msg['payload'] == 'gphoto':\n return msg_enable, msg_enable, msg_disable\n\nreturn msg_enable", "outputs": 3, "x": 560, "y": 440, "wires": [ [ "a0ba1aa77c5c8b7c" ], [ "a42c12e94f65fa01" ], [ "2d76e5617f13cd6c" ] ] }, { "id": "a0ba1aa77c5c8b7c", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "2aea1727dbea76ce", "4f212b44aa487945", "65cef204b16f8741", "917a194be245384a" ], "x": 675, "y": 420, "wires": [] }, { "id": "a42c12e94f65fa01", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "2aea1727dbea76ce", "4f212b44aa487945", "65cef204b16f8741", "917a194be245384a" ], "x": 715, "y": 440, "wires": [] }, { "id": "2d76e5617f13cd6c", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "65cef204b16f8741" ], "x": 675, "y": 460, "wires": [] }, { "id": "bd80ec228fb9a86d", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 135, "y": 340, "wires": [ [ "78351089ee9ebeaf", "5fba78ae65eaaf5d", "3492754252645e62", "d16525a31223bc42", "67206663b3881868" ] ] }, { "id": "65b38bfeb3fee710", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 155, "y": 760, "wires": [ [ "cc3cb10f2ea3f8b8" ] ] }, { "id": "d3fc91d87d5d5f62", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 135, "y": 940, "wires": [ [ "725fd0cab0bddc0e" ] ] }, { "id": "cc9c4092edeb43cc", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 135, "y": 1020, "wires": [ [ "27c6b221c90ed9e1", "f393400.d87dcc" ] ] }, { "id": "f0b355967b33dfee", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 175, "y": 1600, "wires": [ [ "91fe20cb16f54293", "2cf946c7aab2cbb4", "4da5f650d3845baa", "6659121906897a1f", "15f02421b30a9ab6", "58928befcc61b1f7", "569829eeff715c33", "c997e60519341afd", "59ecf3a22cd3a669", "27bc56f273360ac7", "9b89eb1eaf333c10", "2e8927be0e235fa1", "f46ced86106306c8", "4339704cd8552eb3", "1ac53bb6150645fe", "0d48bb415c584420", "b6e420121e6466e7" ] ] }, { "id": "d7c1fb4c028b21a5", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 155, "y": 2280, "wires": [ [ "d5308090f2b7971a", "694d1068bea15171", "cec3e5e78a40476b", "6f524f9370a18482", "1f87f473e327c3cc", "cff7ac5f1e061855", "cf854461c37ca54f", "ba10e04dd1761692", "a69d216114f908a5", "f02d4a036a225e87", "1efd4a05aee0b86c", "6841e5a392f0fb4f" ] ] }, { "id": "a67c18aaca2f5fa5", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 155, "y": 2900, "wires": [ [ "612cccacda1a65aa", "954db931f87894ee", "6682c8057e89d087", "015be401d08047d2", "1c6c0f8b9ac95659", "dcee66c0d56c6934", "6ec7d85bb17eb159", "4f42d02a3776a006", "5d70f4715c9a5ae1", "3b126549c03a872e", "58bbe9fc41e0d7b9" ] ] }, { "id": "c6d3821bc7f43f8e", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Reset default", "group": "4fe6b4c0ade0938a", "order": 14, "width": 6, "height": 1, "passthru": false, "label": "Restore default settings", "tooltip": "", "color": "red", "bgcolor": "", "className": "", "icon": "", "payload": "This can not be undone!", "payloadType": "str", "topic": "Restore default settings?", "topicType": "str", "x": 930, "y": 300, "wires": [ [ "e4be21c38b57f560" ] ] }, { "id": "e4be21c38b57f560", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 1090, "y": 300, "wires": [ [ "9f30de04ced693d3" ] ] }, { "id": "9f30de04ced693d3", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.overwrite = true\nif(msg.payload == \"Yes\"){\n return msg}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1230, "y": 300, "wires": [ [ "80bccc884b0be297" ] ] }, { "id": "80bccc884b0be297", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "38783aea9cc317a6" ], "x": 1325, "y": 300, "wires": [] }, { "id": "34b685aff2080d31", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "boot-cam", "func": "from OpenScan import load_str\n\ncamera_modules = ('imx519', 'imx219', 'ov5647', 'imx477', 'imx378', 'ov9281', 'imx290a', 'imx290b')\n\npt1 = \"[all]\\n\\ncamera_auto_detect=0\\ngpu_mem=256\\ndtoverlay=vc4-fkms-v3d\\ndtoverlay=\"\npt3 = \",media-controller=1\\n\"\n\nwith open('/boot/config.txt', 'r') as file:\n config = file.read()\n\ncamera = load_str('camera')\nif camera not in camera_modules:\n msg['payload'] = 'no changes'\n return\n\nif camera == 'imx290a':\n camera = 'imx290,clock-frequency=37125000'\nelif camera == 'imx290b':\n camera = 'imx290,clock-frequency=74250000'\n\nconfig_keep = config.split('[all]\\n')[0]\nconfig_new = config_keep + pt1 + camera + pt3\n\nwith open('/boot/config.txt', 'w') as file:\n file.write(config_new)\n\nmsg['topic'] = 'Camera configuration changed'\nmsg['payload'] = 'Please restart the device'\n\nreturn msg", "outputs": 1, "x": 680, "y": 500, "wires": [ [ "68cba0c530c6def6" ] ] }, { "id": "68cba0c530c6def6", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 830, "y": 500, "wires": [ [] ] }, { "id": "f304680180a23479", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": true, "className": "", "topic": "", "name": "Info", "x": 1010, "y": 120, "wires": [ [] ] }, { "id": "0d48bb415c584420", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'turntable_mode'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1640, "wires": [ [ "ce215e159ce7267f" ] ] }, { "id": "ce215e159ce7267f", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Turntable Mode", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 2, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 440, "y": 1640, "wires": [ [ "f95f528dec31425c" ] ] }, { "id": "f95f528dec31425c", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'turntable_mode'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1640, "wires": [ [] ] }, { "id": "4ebe5baece5ce9f2", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "preview_resolution", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 5, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.5", "max": "10", "step": "0.5", "className": "", "x": 450, "y": 2280, "wires": [ [ "60a415fff23cb55e" ] ] }, { "id": "9ed0498cceceedde", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 4, "width": 3, "height": 1, "name": "preview_res", "label": "Preview Resolution (Mpx)", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2280, "wires": [] }, { "id": "1efd4a05aee0b86c", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_preview_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data)/1000000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2280, "wires": [ [ "4ebe5baece5ce9f2" ] ] }, { "id": "60a415fff23cb55e", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_preview_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload*1000000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2280, "wires": [ [] ] }, { "id": "6f3d403e157163e4", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "Camera", "label": "", "tooltip": "", "place": "Select option", "group": "1f7f7e1e24f5ad9b", "order": 5, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Pi Cam v1 - 5mp", "value": "ov5647", "type": "str" }, { "label": "Pi Cam v2 - 8mp", "value": "imx219", "type": "str" }, { "label": "Pi Cam HQ - 12.3mp", "value": "imx477", "type": "str" }, { "label": "Arducam IMX519 - 16mp", "value": "imx519", "type": "str" }, { "label": "IMX290 a", "value": "imx290a", "type": "str" }, { "label": "IMX290 b", "value": "imx290b", "type": "str" }, { "label": "IMX378", "value": "imx378", "type": "str" }, { "label": "OV9281", "value": "ov9281", "type": "str" }, { "label": "DSLR (gphoto)", "value": "gphoto", "type": "str" }, { "label": "USB Webcam", "value": "usb_webcam", "type": "str" }, { "label": "External Camera", "value": "external", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 400, "y": 460, "wires": [ [ "6d68cccec646e0a0", "4058a31e942e8f95" ] ] }, { "id": "c6138801b30f091d", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "model", "label": "", "tooltip": "", "place": "Select option", "group": "1f7f7e1e24f5ad9b", "order": 3, "width": 4, "height": 1, "passthru": true, "multiple": false, "options": [ { "label": "Please Select", "value": "None", "type": "str" }, { "label": "OpenScan Mini", "value": "OSMini", "type": "str" }, { "label": "OpenScan Classic", "value": "OSClassic", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 390, "y": 580, "wires": [ [ "896242c5a7e50fa7" ] ] }, { "id": "4da67c23c7a543a0", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "1f7f7e1e24f5ad9b", "order": 4, "width": 2, "height": 1, "name": "", "label": "Camera", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 840, "y": 460, "wires": [] }, { "id": "1fed8676078ea9a7", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "1f7f7e1e24f5ad9b", "order": 2, "width": 2, "height": 1, "name": "", "label": "Model", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 730, "y": 580, "wires": [] }, { "id": "a4b7eea9a9736b0a", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Update&Info", "group": "ddbd496e.93a288", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Update&Log

Status

See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on Github.com for details and the source code.

Updatetype

- stable: latest well-tested and mostly bug-free version for the OpenScanMini or Classic and various cameras

- beta: stable version + some experimental and new features, which might bring joy and some new bugs as well

- mini: very simplified firmware for the OpenScanMini + Arducam IMX519

Auto-Check update availability

Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on Github.com

This option is activated by default.

Check Updates

Alternatively, you can check for updates manually at any time by pressing this button.

Download Error Log

In case you encounter any errors with your device, please download the error log text and send a copy to info@openscan.eu or create an issue on Github.com

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 750, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "b6e420121e6466e7", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'routine_secondpass'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 1600, "wires": [ [ "ab8d5cfe9190bb5f" ] ] }, { "id": "ab8d5cfe9190bb5f", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Second pass", "tooltip": "", "group": "d49a6dfd7fb17096", "order": 3, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 430, "y": 1600, "wires": [ [ "fa51327f0140b045" ] ] }, { "id": "fa51327f0140b045", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'routine_secondpass'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 1600, "wires": [ [] ] }, { "id": "6841e5a392f0fb4f", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'cam_output_downscale'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2320, "wires": [ [ "110216d678fad14f" ] ] }, { "id": "110216d678fad14f", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Downscale output", "tooltip": "", "group": "93aadb71dee6d977", "order": 6, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 450, "y": 2320, "wires": [ [ "214d548d564f8ba2" ] ] }, { "id": "214d548d564f8ba2", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_output_downscale'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \nmsg.enabled = msg.payload\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2320, "wires": [ [ "1becbff4884b8c1a" ] ] }, { "id": "8be1ca844a6caa54", "type": "ui_slider", "z": "017bd4e4a428bee5", "name": "output_resolution", "label": "", "tooltip": "", "group": "93aadb71dee6d977", "order": 8, "width": 3, "height": 1, "passthru": false, "outs": "end", "topic": "", "topicType": "str", "min": "0.5", "max": "20", "step": "0.5", "className": "", "x": 450, "y": 2360, "wires": [ [ "a6b2c0a0604ccf14" ] ] }, { "id": "9ac09d89d791e953", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "93aadb71dee6d977", "order": 7, "width": 3, "height": 1, "name": "image_res", "label": "Output Resolution (Mpx)", "format": "", "layout": "row-spread", "className": "", "x": 770, "y": 2360, "wires": [] }, { "id": "1becbff4884b8c1a", "type": "function", "z": "017bd4e4a428bee5", "name": "loadI", "func": "var file = 'cam_output_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data)/1000000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 2360, "wires": [ [ "8be1ca844a6caa54" ] ] }, { "id": "a6b2c0a0604ccf14", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'cam_output_resolution'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload*1000000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 630, "y": 2360, "wires": [ [] ] }, { "id": "f358de1e64b491bb", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "b30d918661392ab3", "44c598049cd533fd" ], "x": 635, "y": 620, "wires": [] }, { "id": "b0629875a30ae1d7", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "get update", "func": "import json\nimport requests\nfrom OpenScan import load_str\n\nif not msg['payload']:\n msg['status'] = '--READY--'\n return msg\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'no internet connection'\n return msg\n\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg['status'] = 'checking updates'\n\nreturn msg, msg", "outputs": 2, "x": 350, "y": 200, "wires": [ [ "1bbe2d769f42c313" ], [ "fefe45404bdb19c4" ] ] }, { "id": "c7b6d05a62172432", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "ddbd496e.93a288", "order": 3, "width": 0, "height": 0, "name": "", "label": "Status:", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 170, "y": 100, "wires": [] }, { "id": "fefe45404bdb19c4", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "check files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str\n\nscope = load_str('update_type')\nmsg['scope'] = scope\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_old = updatepath + 'update.json'\n\ncounter = 0\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n if os.path.isfile(temp):\n filesize = os.path.getsize(temp)\n msg[scope][i]['filesize2'] = filesize\n if filesize == msg[scope][i]['filesize']:\n msg[scope][i]['update'] = False\n continue\n msg[scope][i]['update'] = True\n\n counter += 1\n\nif counter == 0:\n msg['status'] = 'No new update available'\nelse:\n msg['status'] = 'New update available'\n msg['topic'] = msg['status']\n msg['payload'] = 'Install & reboot now?'\n\nmsg['counter'] = counter\n\nreturn msg\n", "outputs": 1, "x": 510, "y": 220, "wires": [ [ "1bbe2d769f42c313", "ae92a328af306ebb" ] ] }, { "id": "d0104e0163745993", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "", "links": [ "960912e90ba5b5bc" ], "x": 75, "y": 140, "wires": [ [ "38cbf7965d1c1834", "49f1ecb29a3f84f4" ] ] }, { "id": "51cd8c8643e6b46a", "type": "ui_switch", "z": "c8e7ecb5849edb9a", "name": "", "label": "Auto-check update availability", "tooltip": "", "group": "ddbd496e.93a288", "order": 6, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 370, "y": 140, "wires": [ [ "1ab4c6b4b232a022" ] ] }, { "id": "38cbf7965d1c1834", "type": "function", "z": "c8e7ecb5849edb9a", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 140, "wires": [ [ "51cd8c8643e6b46a" ] ] }, { "id": "1ab4c6b4b232a022", "type": "function", "z": "c8e7ecb5849edb9a", "name": "write", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 570, "y": 140, "wires": [ [] ] }, { "id": "ae92a328af306ebb", "type": "ui_toast", "z": "c8e7ecb5849edb9a", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "NO", "cancel": "YES", "raw": false, "className": "", "topic": "", "name": "", "x": 670, "y": 220, "wires": [ [ "2de63e8e3ae5fb0c", "929281fef53e09f8" ] ] }, { "id": "cbd0afc4aa7b302a", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "update status", "links": [ "1bbe2d769f42c313", "42061b28cff81f99" ], "x": 75, "y": 100, "wires": [ [ "c7b6d05a62172432", "c94623ddd9d95f78" ] ] }, { "id": "1bbe2d769f42c313", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "cbd0afc4aa7b302a" ], "x": 625, "y": 180, "wires": [] }, { "id": "7cf60615d93e696b", "type": "ui_button", "z": "c8e7ecb5849edb9a", "name": "", "group": "ddbd496e.93a288", "order": 7, "width": 6, "height": 1, "passthru": false, "label": "Check Updates", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 140, "y": 220, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "2de63e8e3ae5fb0c", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "download files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str, save\n\n\nscope = msg['scope']\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\n\nif msg['payload'] != 'YES':\n return\n\ncounter = 0\n\nfor i in msg[scope]:\n if msg[scope][i]['update'] == False:\n continue\n \n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n \n r = requests.get(url + msg[scope][i]['src'])\n if r.status_code != 200:\n msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\n return msg\n with open(temp, 'wb+') as file:\n file.write(r.content)\n shutil.copy(temp, msg[scope][i]['dst'])\n \n if msg[scope][i]['dst'] == '/boot/config.txt':\n save('camera','')\n \n counter += 1\n\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\n\nif counter == msg['counter']:\n updatepath_temp = updatepath + 'update_temp.json'\n updatepath_old = updatepath + 'update.json'\n shutil.move(updatepath_temp, updatepath_old)\n\nreturn msg\n", "outputs": 1, "x": 840, "y": 220, "wires": [ [ "42061b28cff81f99", "fe3a855fee9e28c6" ] ] }, { "id": "929281fef53e09f8", "type": "function", "z": "c8e7ecb5849edb9a", "name": "msg", "func": "if (msg.payload == 'YES'){\n msg.status = 'Installing updates'\n return msg}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 810, "y": 180, "wires": [ [ "42061b28cff81f99" ] ] }, { "id": "42061b28cff81f99", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "cbd0afc4aa7b302a" ], "x": 955, "y": 180, "wires": [] }, { "id": "49f1ecb29a3f84f4", "type": "function", "z": "c8e7ecb5849edb9a", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\n\nmsg.payload = data\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 180, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "fe3a855fee9e28c6", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "9bb0adbd716ce347" ], "x": 955, "y": 220, "wires": [] }, { "id": "5e7d5e4335d37794", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "", "links": [ "960912e90ba5b5bc" ], "x": 55, "y": 360, "wires": [ [ "2bb5fe78e09fec8a" ] ] }, { "id": "2bb5fe78e09fec8a", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "msg", "func": "\nfrom subprocess import getoutput\nimport os\n\nmsg['os'] = getoutput(\"cat /etc/os-release | grep -i 'PRETTY_NAME'\")[13:-1]\nmsg['device'] = getoutput(\"cat /proc/device-tree/model\")\nmsg['flask'] = getoutput(\"systemctl status flask |grep -i 'Active:'\").split(' ')[6]\nmsg['osdate'] = getoutput(\"vcgencmd version\").split('\\n')[0]\nmsg['temp'] = getoutput(\"vcgencmd measure_temp\").split('=')[1]\ncpu_total = getoutput(\"free -m | head -n2 | tail -n1 |awk '{print $2}'\")\ncpu_used = getoutput(\"free -m | head -n2 | tail -n1 |awk '{print $3}'\")\nswap_total = getoutput(\"free -m | head -n3 | tail -n1 |awk '{print $2}'\")\nswap_used = getoutput(\"free -m | head -n3 | tail -n1 |awk '{print $3}'\")\ndiskspace_used = getoutput(\"df -h / | tail -n1 |awk '{print $3}'\")\ndiskspace_total = getoutput(\"df -h / | tail -n1 |awk '{print $2}'\")\n\nmsg['cpu'] = cpu_used + '/' + cpu_total + 'MB'\nmsg['swap'] = swap_used + '/' + swap_total + 'MB'\nmsg['diskspace'] =diskspace_used + '/' + diskspace_total\n\nif msg['flask'] == 'inactive':\n os.system('systemctl restart flask')\n\nreturn msg", "outputs": 1, "x": 170, "y": 360, "wires": [ [ "dbc77052ac950624", "d97c3068ef5fef96", "73a3b828f862312b", "901e31453b2bdff8", "f983854748ee4763", "5347c7c517f5e8c7", "3a5016f7003cd72c", "6d720c4a4ecd9475", "6438b7d060a70d81" ] ] }, { "id": "d97c3068ef5fef96", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 2, "width": 0, "height": 0, "name": "", "label": "OS:", "format": "{{msg.os}}", "layout": "row-spread", "className": "", "x": 450, "y": 400, "wires": [] }, { "id": "73a3b828f862312b", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 8, "width": 0, "height": 0, "name": "", "label": "Flask:", "format": "{{msg.flask}}", "layout": "row-spread", "className": "", "x": 450, "y": 440, "wires": [] }, { "id": "dbc77052ac950624", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 1, "width": 0, "height": 0, "name": "", "label": "Device:", "format": "{{msg.device}}", "layout": "row-spread", "className": "", "x": 460, "y": 360, "wires": [] }, { "id": "4c7fa5b5b27b83a5", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "create beta new", "func": "import json\nimport requests\nimport shutil\n\nscope = 'main'\n#scope = 'beta'\n\nupdatepath = '/home/pi/OpenScan/tmp/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\n\n## load update.json\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'download update.json failed'\n return msg\n\nmsg = {}\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg2 = msg.copy()\ndel msg[scope]\n\nmsg[scope]={}\nmsg[scope]['1'] = {}\nmsg[scope]['1']['src'] = scope + '/fla.py'\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\n\nmsg[scope]['2'] = {}\nmsg[scope]['2']['src'] = scope + '/Arducam.py'\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/Arducam.py'\n\nmsg[scope]['3'] = {}\nmsg[scope]['3']['src'] = scope + '/OpenScan.py'\nmsg[scope]['3']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\n\nmsg[scope]['4'] = {}\nmsg[scope]['4']['src'] = scope + '/config.txt'\nmsg[scope]['4']['dst'] = '/boot/config.txt'\n\nmsg[scope]['5'] = {}\nmsg[scope]['5']['src'] = scope + '/flows.json'\nmsg[scope]['5']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\n\nmsg[scope]['6'] = {}\nmsg[scope]['6']['src'] = scope + '/settings.js'\nmsg[scope]['6']['dst'] = '/root/.node-red/settings.js'\n\nmsg[scope]['7'] = {}\nmsg[scope]['7']['src'] = 'files/logo.jpg'\nmsg[scope]['7']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\n\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n filesize = os.path.getsize(filepath)\n msg[scope][i]['filesize'] = filesize\n\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\n os.system('rm -r /home/pi/OpenScan/tmp/update') \nos.makedirs('/home/pi/OpenScan/tmp/update/')\n\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\n json.dump(msg, f, indent=4)\n\nfor i in msg[scope]:\n if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\n os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\n shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\n\nmsg['payload'] = 'created with scope: ' + scope\n\nreturn msg", "outputs": 1, "x": 300, "y": 820, "wires": [ [ "50f6fb8adf0249d7" ] ] }, { "id": "80175eb8dc6ad009", "type": "inject", "z": "c8e7ecb5849edb9a", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 140, "y": 820, "wires": [ [ "4c7fa5b5b27b83a5" ] ] }, { "id": "3f42560297fe6978", "type": "ui_template", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "name": "Download LOG", "order": 9, "width": 6, "height": 1, "format": "\n
Download error log\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 140, "y": 760, "wires": [ [] ] }, { "id": "c94623ddd9d95f78", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "get update", "func": "from OpenScan import save\n\nif msg['status'] == \"No new update available\":\n save('updateable',False)\nelif msg['status'] == \"New update available\":\n save('updateable',True)\n", "outputs": 1, "x": 170, "y": 60, "wires": [ [] ] }, { "id": "39a502b38837273d", "type": "link in", "z": "c8e7ecb5849edb9a", "name": "", "links": [ "1e7457ea9c2c5e09" ], "x": 205, "y": 260, "wires": [ [ "b0629875a30ae1d7" ] ] }, { "id": "901e31453b2bdff8", "type": "delay", "z": "c8e7ecb5849edb9a", "name": "", "pauseType": "delay", "timeout": "10", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 180, "y": 400, "wires": [ [ "2bb5fe78e09fec8a" ] ] }, { "id": "f983854748ee4763", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 3, "width": 0, "height": 0, "name": "", "label": "", "format": "{{msg.osdate}}", "layout": "row-spread", "className": "", "x": 450, "y": 480, "wires": [] }, { "id": "5347c7c517f5e8c7", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 4, "width": 0, "height": 0, "name": "", "label": "CPU temp:", "format": "{{msg.temp}}", "layout": "row-spread", "className": "", "x": 470, "y": 520, "wires": [] }, { "id": "3a5016f7003cd72c", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 5, "width": 0, "height": 0, "name": "", "label": "CPU memory:", "format": "{{msg.cpu}}", "layout": "row-spread", "className": "", "x": 480, "y": 560, "wires": [] }, { "id": "6d720c4a4ecd9475", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 6, "width": 0, "height": 0, "name": "", "label": "Swap memory:", "format": "{{msg.swap}}", "layout": "row-spread", "className": "", "x": 480, "y": 600, "wires": [] }, { "id": "6438b7d060a70d81", "type": "ui_text", "z": "c8e7ecb5849edb9a", "group": "3ce32450.e0cffc", "order": 7, "width": 0, "height": 0, "name": "", "label": "Diskspace:", "format": "{{msg.diskspace}}", "layout": "row-spread", "className": "", "x": 470, "y": 640, "wires": [] }, { "id": "d7362e6e0ec7bdaa", "type": "inject", "z": "c8e7ecb5849edb9a", "name": "", "props": [ { "p": "overwrite", "v": "true", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 130, "y": 900, "wires": [ [ "4ce127c61c3c5966", "beacc3dc5398fa79" ] ] }, { "id": "4ce127c61c3c5966", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "prepare image creation", "func": "import os\n\n#factory reset, reset wpa, create wpa in boot, rm files\n#should be done before creating a new raspbian image\n\nbasepath = '/home/pi/OpenScan/'\n\n#remove files\n\ndir = basepath + 'scans/'\n\nfor i in ['scans/','tmp/']:\n os.system('rm -r ' + basepath + i)\n os.mkdir(basepath + i)\n\n#delete wifi\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\nwith open(temp_dir, 'w+') as file:\n file.write('update_config=1\\nctrl_interface=DIR=/var/run/wpa_supplicant\\ncountry=de\\n\\n')\nos.system('mv '+ temp_dir + ' ' + wpa_dir)\nos.system('wpa_cli -i wlan0 reconfigure')\n\n#create new wpa_supplicant.conf\nwith open('/boot/wpa_supplicant.conf','w+') as file:\n file.write('country=de\\nupdate_config=1\\nctrl_interface=/var/run/wpa_supplicant\\n\\nnetwork={\\n scan_ssid=1\\n ssid=\"wlan name\"\\n psk=\"xxxx\"\\n}')\n\n#rm tmp dir\n\n\n#stop photos:\nos.system('systemctl stop flask')\nos.system('rm -r ' + basepath + 'tmp')\nos.system('mkdir ' + basepath + 'tmp')\n\nos.system('systemctl stop nodered')\n\n#reset factory\n\n", "outputs": 1, "x": 330, "y": 900, "wires": [ [] ] }, { "id": "beacc3dc5398fa79", "type": "link out", "z": "c8e7ecb5849edb9a", "name": "", "mode": "link", "links": [ "38783aea9cc317a6" ], "x": 235, "y": 940, "wires": [] }, { "id": "8d012912f302be85", "type": "ui_button", "z": "c8e7ecb5849edb9a", "name": "", "group": "ddbd496e.93a288", "order": 8, "width": 6, "height": 1, "passthru": false, "label": "Show Details/Changelog", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 170, "y": 300, "wires": [ [ "5242607a723cc628" ] ] }, { "id": "5242607a723cc628", "type": "python3-function", "z": "c8e7ecb5849edb9a", "name": "Changelog", "func": "import requests\n\ntempfile = '/home/pi/OpenScan/tmp/changelog'\n\nurl = 'https://raw.githubusercontent.com/OpenScan-org/OpenScan-Doc/main/docs/changelog.md'\nr = requests.get(url, allow_redirects=False)\n\nwith open(tempfile,'wb') as file:\n file.write(r.content)\n \nwith open(tempfile, 'r') as file:\n text = file.read()\n \ntext = text.replace('\\n','
').replace('*', '  - ')\nmsg['payload'] = text\n\nreturn msg", "outputs": 1, "x": 390, "y": 300, "wires": [ [ "573722197b15bf84" ] ] }, { "id": "573722197b15bf84", "type": "ui_toast", "z": "c8e7ecb5849edb9a", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": true, "className": "", "topic": "", "name": "", "x": 570, "y": 300, "wires": [ [] ] }, { "id": "50f6fb8adf0249d7", "type": "debug", "z": "c8e7ecb5849edb9a", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 570, "y": 820, "wires": [] } ] ================================================ FILE: update/main/settings.js ================================================ /** * Node-RED Settings created at Mon, 24 Jan 2022 08:17:31 GMT * * It can contain any valid JavaScript code that will get run when Node-RED * is started. * * Lines that start with // are commented out. * Each entry should be separated from the entries above and below by a comma ',' * * For more information about individual settings, refer to the documentation: * https://nodered.org/docs/user-guide/runtime/configuration * * The settings are split into the following sections: * - Flow File and User Directory Settings * - Security * - Server Settings * - Runtime Settings * - Editor Settings * - Node Settings * **/ module.exports = { /******************************************************************************* * Flow File and User Directory Settings * - flowFile * - credentialSecret * - flowFilePretty * - userDir * - nodesDir ******************************************************************************/ /** The file containing the flows. If not set, defaults to flows_.json **/ flowFile: "flows.json", /** By default, credentials are encrypted in storage using a generated key. To * specify your own secret, set the following property. * If you want to disable encryption of credentials, set this property to false. * Note: once you set this property, do not change it - doing so will prevent * node-red from being able to decrypt your existing credentials and they will be * lost. */ credentialSecret: false, /** By default, the flow JSON will be formatted over multiple lines making * it easier to compare changes when using version control. * To disable pretty-printing of the JSON set the following property to false. */ flowFilePretty: true, /** By default, all user data is stored in a directory called `.node-red` under * the user's home directory. To use a different location, the following * property can be used */ //userDir: '/home/nol/.node-red/', userDir: '/home/pi/OpenScan/settings/.node-red/', /** Node-RED scans the `nodes` directory in the userDir to find local node files. * The following property can be used to specify an additional directory to scan. */ //nodesDir: '/home/nol/.node-red/nodes', /******************************************************************************* * Security * - adminAuth * - https * - httpsRefreshInterval * - requireHttps * - httpNodeAuth * - httpStaticAuth ******************************************************************************/ /** To password protect the Node-RED editor and admin API, the following * property can be used. See http://nodered.org/docs/security.html for details. */ //adminAuth: { // type: "credentials", // users: [{ // username: "admin", // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", // permissions: "*" // }] //}, /** The following property can be used to enable HTTPS * This property can be either an object, containing both a (private) key * and a (public) certificate, or a function that returns such an object. * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener * for details of its contents. */ /** Option 1: static object */ //https: { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') //}, /** Option 2: function that returns the HTTP configuration object */ // https: function() { // // This function should return the options object, or a Promise // // that resolves to the options object // return { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') // } // }, /** If the `https` setting is a function, the following setting can be used * to set how often, in hours, the function will be called. That can be used * to refresh any certificates. */ //httpsRefreshInterval : 12, /** The following property can be used to cause insecure HTTP connections to * be redirected to HTTPS. */ //requireHttps: true, /** To password protect the node-defined HTTP endpoints (httpNodeRoot), * including node-red-dashboard, or the static content (httpStatic), the * following properties can be used. * The `pass` field is a bcrypt hash of the password. * See http://nodered.org/docs/security.html#generating-the-password-hash */ //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, /******************************************************************************* * Server Settings * - uiPort * - uiHost * - apiMaxLength * - httpServerOptions * - httpAdminRoot * - httpAdminMiddleware * - httpNodeRoot * - httpNodeCors * - httpNodeMiddleware * - httpStatic ******************************************************************************/ /** the tcp port that the Node-RED web server is listening on */ // uiPort: process.env.PORT || 1880, uiPort: process.env.PORT || 80, /** By default, the Node-RED UI accepts connections on all IPv4 interfaces. * To listen on all IPv6 addresses, set uiHost to "::", * The following property can be used to listen on a specific interface. For * example, the following would only allow connections from the local machine. */ //uiHost: "127.0.0.1", /** The maximum size of HTTP request that will be accepted by the runtime api. * Default: 5mb */ //apiMaxLength: '5mb', /** The following property can be used to pass custom options to the Express.js * server used by Node-RED. For a full list of available options, refer * to http://expressjs.com/en/api.html#app.settings.table */ //httpServerOptions: { }, /** By default, the Node-RED UI is available at http://localhost:1880/ * The following property can be used to specify a different root path. * If set to false, this is disabled. */ //httpAdminRoot: '/admin', httpAdminRoot: '/editor', /** The following property can be used to add a custom middleware function * in front of all admin http routes. For example, to set custom http * headers. It can be a single function or an array of middleware functions. */ // httpAdminMiddleware: function(req,res,next) { // // Set the X-Frame-Options header to limit where the editor // // can be embedded // //res.set('X-Frame-Options', 'sameorigin'); // next(); // }, /** Some nodes, such as HTTP In, can be used to listen for incoming http requests. * By default, these are served relative to '/'. The following property * can be used to specifiy a different root path. If set to false, this is * disabled. */ //httpNodeRoot: '/red-nodes', /** The following property can be used to configure cross-origin resource sharing * in the HTTP nodes. * See https://github.com/troygoode/node-cors#configuration-options for * details on its contents. The following is a basic permissive set of options: */ //httpNodeCors: { // origin: "*", // methods: "GET,PUT,POST,DELETE" //}, /** If you need to set an http proxy please set an environment variable * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. * For example - http_proxy=http://myproxy.com:8080 * (Setting it here will have no effect) * You may also specify no_proxy (or NO_PROXY) to supply a comma separated * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk */ /** The following property can be used to add a custom middleware function * in front of all http in nodes. This allows custom authentication to be * applied to all http in nodes, or any other sort of common request processing. * It can be a single function or an array of middleware functions. */ //httpNodeMiddleware: function(req,res,next) { // // Handle/reject the request, or pass it on to the http in node by calling next(); // // Optionally skip our rawBodyParser by setting this to true; // //req.skipRawBodyParser = true; // next(); //}, /** When httpAdminRoot is used to move the UI to a different root path, the * following property can be used to identify a directory of static content * that should be served at http://localhost:1880/. */ //httpStatic: '/home/nol/node-red-static/', httpStatic: '/home/pi/OpenScan/', /******************************************************************************* * Runtime Settings * - lang * - logging * - contextStorage * - exportGlobalContextKeys * - externalModules ******************************************************************************/ /** Uncomment the following to run node-red in your preferred language. * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko * Some languages are more complete than others. */ // lang: "de", /** Configure the logging output */ logging: { /** Only console logging is currently supported */ console: { /** Level of logging to be recorded. Options are: * fatal - only those errors which make the application unusable should be recorded * error - record errors which are deemed fatal for a particular request + fatal errors * warn - record problems which are non fatal + errors + fatal errors * info - record information about the general running of the application + warn + error + fatal errors * debug - record information which is more verbose than info + info + warn + error + fatal errors * trace - record very detailed logging + debug + info + warn + error + fatal errors * off - turn off all logging (doesn't affect metrics or audit) */ level: "info", /** Whether or not to include metric events in the log output */ metrics: false, /** Whether or not to include audit events in the log output */ audit: false } }, /** Context Storage * The following property can be used to enable context storage. The configuration * provided here will enable file-based context that flushes to disk every 30 seconds. * Refer to the documentation for further options: https://nodered.org/docs/api/context/ */ //contextStorage: { // default: { // module:"localfilesystem" // }, //}, /** `global.keys()` returns a list of all properties set in global context. * This allows them to be displayed in the Context Sidebar within the editor. * In some circumstances it is not desirable to expose them to the editor. The * following property can be used to hide any property set in `functionGlobalContext` * from being list by `global.keys()`. * By default, the property is set to false to avoid accidental exposure of * their values. Setting this to true will cause the keys to be listed. */ exportGlobalContextKeys: false, /** Configure how the runtime will handle external npm modules. * This covers: * - whether the editor will allow new node modules to be installed * - whether nodes, such as the Function node are allowed to have their * own dynamically configured dependencies. * The allow/denyList options can be used to limit what modules the runtime * will install/load. It can use '*' as a wildcard that matches anything. */ externalModules: { // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ // palette: { /** Configuration for the Palette Manager */ // allowInstall: true, /** Enable the Palette Manager in the editor */ // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ // allowList: [], // denyList: [] // }, // modules: { /** Configuration for node-specified modules */ // allowInstall: true, // allowList: [], // denyList: [] // } }, /******************************************************************************* * Editor Settings * - disableEditor * - editorTheme ******************************************************************************/ /** The following property can be used to disable the editor. The admin API * is not affected by this option. To disable both the editor and the admin * API, use either the httpRoot or httpAdminRoot properties */ //disableEditor: false, /** Customising the editor * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes * for all available options. */ editorTheme: { /** The following property can be used to set a custom theme for the editor. * See https://github.com/node-red-contrib-themes/theme-collection for * a collection of themes to chose from. */ //theme: "", palette: { /** The following property can be used to order the categories in the editor * palette. If a node's category is not in the list, the category will get * added to the end of the palette. * If not set, the following default order is used: */ //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], }, projects: { /** To enable the Projects feature, set this value to true */ enabled: false, workflow: { /** Set the default projects workflow mode. * - manual - you must manually commit changes * - auto - changes are automatically committed * This can be overridden per-user from the 'Git config' * section of 'User Settings' within the editor */ mode: "manual" } }, codeEditor: { /** Select the text editor component used by the editor. * Defaults to "ace", but can be set to "ace" or "monaco" */ lib: "ace", options: { /** The follow options only apply if the editor is set to "monaco" * * theme - must match the file name of a theme in * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme" */ theme: "vs", /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc. * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html */ //fontSize: 14, //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", //fontLigatures: true, } } }, /******************************************************************************* * Node Settings * - fileWorkingDirectory * - functionGlobalContext * - functionExternalModules * - nodeMessageBufferMaxLength * - ui (for use with Node-RED Dashboard) * - debugUseColors * - debugMaxLength * - execMaxBufferSize * - httpRequestTimeout * - mqttReconnectTime * - serialReconnectTime * - socketReconnectTime * - socketTimeout * - tcpMsgQueueSize * - inboundWebSocketTimeout * - tlsConfigDisableLocalFiles * - webSocketNodeVerifyClient ******************************************************************************/ /** The working directory to handle relative file paths from within the File nodes * defaults to the working directory of the Node-RED process. */ //fileWorkingDirectory: "", /** Allow the Function node to load additional npm modules directly */ functionExternalModules: true, /** The following property can be used to set predefined values in Global Context. * This allows extra node modules to be made available with in Function node. * For example, the following: * functionGlobalContext: { os:require('os') } * will allow the `os` module to be accessed in a Function node using: * global.get("os") */ functionGlobalContext: { os:require('os'), path:require('path'), fs:require('fs'), }, /** The maximum number of messages nodes will buffer internally as part of their * operation. This applies across a range of nodes that operate on message sequences. * defaults to no limit. A value of 0 also means no limit is applied. */ //nodeMessageBufferMaxLength: 0, /** If you installed the optional node-red-dashboard you can set it's path * relative to httpNodeRoot * Other optional properties include * readOnly:{boolean}, * middleware:{function or array}, (req,res,next) - http middleware * ioMiddleware:{function or array}, (socket,next) - socket.io middleware */ //ui: { path: "ui" }, ui: { path: "" }, /** Colourise the console output of the debug node */ //debugUseColors: true, /** The maximum length, in characters, of any message sent to the debug sidebar tab */ debugMaxLength: 1000, /** Maximum buffer size for the exec node. Defaults to 10Mb */ //execMaxBufferSize: 10000000, /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */ //httpRequestTimeout: 120000, /** Retry time in milliseconds for MQTT connections */ mqttReconnectTime: 15000, /** Retry time in milliseconds for Serial port connections */ serialReconnectTime: 15000, /** Retry time in milliseconds for TCP socket connections */ //socketReconnectTime: 10000, /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */ //socketTimeout: 120000, /** Maximum number of messages to wait in queue while attempting to connect to TCP socket * defaults to 1000 */ //tcpMsgQueueSize: 2000, /** Timeout in milliseconds for inbound WebSocket connections that do not * match any configured node. Defaults to 5000 */ //inboundWebSocketTimeout: 5000, /** To disable the option for using local files for storing keys and * certificates in the TLS configuration node, set this to true. */ //tlsConfigDisableLocalFiles: true, /** The following property can be used to verify websocket connection attempts. * This allows, for example, the HTTP request headers to be checked to ensure * they include valid authentication information. */ //webSocketNodeVerifyClient: function(info) { // /** 'info' has three properties: // * - origin : the value in the Origin header // * - req : the HTTP request // * - secure : true if req.connection.authorized or req.connection.encrypted is set // * // * The function should return true if the connection should be accepted, false otherwise. // * // * Alternatively, if this function is defined to accept a second argument, callback, // * it can be used to verify the client asynchronously. // * The callback takes three arguments: // * - result : boolean, whether to accept the connection or not // * - code : if result is false, the HTTP error status to return // * - reason: if result is false, the HTTP reason string to return // */ //}, } ================================================ FILE: update/mini/Arducam.py ================================================ import time import os try: import v4l2 except Exception as e: print(e) print("Try to install v4l2-fix") try: from pip import main as pipmain except ImportError: from pip._internal import main as pipmain pipmain(['install', 'v4l2-fix']) print("\nTry to run the focus program again.") exit(0) import fcntl import errno # # Type # v4l2.V4L2_CTRL_TYPE_INTEGER # v4l2.V4L2_CTRL_TYPE_BOOLEAN # v4l2.V4L2_CTRL_TYPE_MENU # v4l2.V4L2_CTRL_TYPE_BUTTON # v4l2.V4L2_CTRL_TYPE_INTEGER64 # v4l2.V4L2_CTRL_TYPE_CTRL_CLASS # # Flags # v4l2.V4L2_CTRL_FLAG_DISABLED # v4l2.V4L2_CTRL_FLAG_GRABBED # v4l2.V4L2_CTRL_FLAG_READ_ONLY # v4l2.V4L2_CTRL_FLAG_UPDATE # v4l2.V4L2_CTRL_FLAG_INACTIVE # v4l2.V4L2_CTRL_FLAG_SLIDER def assert_valid_queryctrl(queryctrl): return queryctrl.type & ( v4l2.V4L2_CTRL_TYPE_INTEGER | v4l2.V4L2_CTRL_TYPE_BOOLEAN | v4l2.V4L2_CTRL_TYPE_MENU | v4l2.V4L2_CTRL_TYPE_BUTTON | v4l2.V4L2_CTRL_TYPE_INTEGER64 | v4l2.V4L2_CTRL_TYPE_CTRL_CLASS | 7 | 8 | 9 ) and queryctrl.flags & ( v4l2.V4L2_CTRL_FLAG_DISABLED | v4l2.V4L2_CTRL_FLAG_GRABBED | v4l2.V4L2_CTRL_FLAG_READ_ONLY | v4l2.V4L2_CTRL_FLAG_UPDATE | v4l2.V4L2_CTRL_FLAG_INACTIVE | v4l2.V4L2_CTRL_FLAG_SLIDER ) def get_device_controls_menu(fd, queryctrl): querymenu = v4l2.v4l2_querymenu(queryctrl.id, queryctrl.minimum) while querymenu.index <= queryctrl.maximum: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYMENU, querymenu) yield querymenu querymenu.index += 1 def get_device_controls_by_class(fd, control_class): # enumeration by control class queryctrl = v4l2.v4l2_queryctrl(control_class | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) while True: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) except IOError as e: assert e.errno == errno.EINVAL break if v4l2.V4L2_CTRL_ID2CLASS(queryctrl.id) != control_class: break yield queryctrl queryctrl = v4l2.v4l2_queryctrl(queryctrl.id | v4l2.V4L2_CTRL_FLAG_NEXT_CTRL) def getdict(struct): val = dict((field, getattr(struct, field)) for field, _ in struct._fields_) val.pop("reserved") return val def get_device_controls(fd): # original enumeration method queryctrl = v4l2.v4l2_queryctrl(v4l2.V4L2_CID_BASE) while queryctrl.id < v4l2.V4L2_CID_LASTP1: try: fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCTRL, queryctrl) print(queryctrl.name) except IOError as e: # this predefined control is not supported by this device assert e.errno == errno.EINVAL queryctrl.id += 1 continue queryctrl = v4l2.v4l2_queryctrl(queryctrl.id + 1) def get_ctrls(vd): ctrls = [] # enumeration by control class for class_ in (v4l2.V4L2_CTRL_CLASS_USER, v4l2.V4L2_CTRL_CLASS_MPEG, v4l2.V4L2_CTRL_CLASS_CAMERA): for queryctrl in get_device_controls_by_class(vd, class_): ctrl = getdict(queryctrl) if queryctrl.type == v4l2.V4L2_CTRL_TYPE_MENU: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): # print(querymenu.name) ctrl["menu"].append(querymenu.name) if queryctrl.type == 9: ctrl["menu"] = [] for querymenu in get_device_controls_menu(vd, queryctrl): ctrl["menu"].append(querymenu.index) ctrls.append(ctrl) return ctrls def set_ctrl(vd, id, value): ctrl = v4l2.v4l2_control() ctrl.id = id ctrl.value = value try: fcntl.ioctl(vd, v4l2.VIDIOC_S_CTRL, ctrl) except IOError as e: print(e) def get_ctrl(vd, id): ctrl = v4l2.v4l2_control() ctrl.id = id try: fcntl.ioctl(vd, v4l2.VIDIOC_G_CTRL, ctrl) except IOError as e: print(e) return None return ctrl.value class Focuser: FOCUS_ID = 0x009a090a dev = None def __init__(self, dev=0): self.focus_value = 0 self.dev = dev if type(dev) == int or (type(dev) == str and dev.isnumeric()): self.dev = "/dev/video{}".format(dev) self.fd = open(self.dev, 'r') self.ctrls = get_ctrls(self.fd) self.hasFocus = False for ctrl in self.ctrls: if ctrl['id'] == Focuser.FOCUS_ID: self.hasFocus = True self.opts[Focuser.OPT_FOCUS]["MIN_VALUE"] = ctrl['minimum'] self.opts[Focuser.OPT_FOCUS]["MAX_VALUE"] = ctrl['maximum'] self.opts[Focuser.OPT_FOCUS]["DEF_VALUE"] = ctrl['default'] self.focus_value = get_ctrl(self.fd, Focuser.FOCUS_ID) if not self.hasFocus: raise RuntimeError("Device {} has no focus_absolute control.".format(self.dev)) def read(self): return self.focus_value def write(self, value): self.focus_value = value # os.system("v4l2-ctl -d {} -c focus_absolute={}".format(self.dev, value)) set_ctrl(self.fd, Focuser.FOCUS_ID, value) OPT_BASE = 0x1000 OPT_FOCUS = OPT_BASE | 0x01 OPT_ZOOM = OPT_BASE | 0x02 OPT_MOTOR_X = OPT_BASE | 0x03 OPT_MOTOR_Y = OPT_BASE | 0x04 OPT_IRCUT = OPT_BASE | 0x05 opts = { OPT_FOCUS : { "MIN_VALUE": 0, "MAX_VALUE": 1000, "DEF_VALUE": 0, }, } def reset(self,opt,flag = 1): info = self.opts[opt] if info == None or info["DEF_VALUE"] == None: return self.set(opt,info["DEF_VALUE"]) def get(self,opt,flag = 0): info = self.opts[opt] return self.read() def set(self,opt,value,flag = 1): info = self.opts[opt] if value > info["MAX_VALUE"]: value = info["MAX_VALUE"] elif value < info["MIN_VALUE"]: value = info["MIN_VALUE"] self.write(value) print("write: {}".format(value)) def __del__(self): self.fd.close() pass ================================================ FILE: update/mini/OpenScan.py ================================================ basepath = '/home/pi/OpenScan/' from os.path import isfile def load_bool(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') if value == '1' or value == 'True' or value =='true': value = True else: value = False return value def load_str(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = file.read().replace('\n','') return value def load_int(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = int(file.read().replace('\n','')) return value def load_float(name): filename = basepath+'settings/'+name if not isfile(filename): return with open(filename, 'r') as file: value = float(file.read().replace('\n','')) return value def save(name, value): filename = basepath+'settings/'+name with open(filename, 'w+') as file: file.write(str(value)) return def OpenScanCloud(cmd, msg): from requests import get osc_user = 'openscan' osc_pw = 'free' osc_server = 'http://openscanfeedback.dnsuser.de:1334/' try: r = get(osc_server + cmd, auth=(osc_user, osc_pw), params=msg) except: r = type('obj', (object,), {'status_code' : 404, 'text':None}) return r def camera(cmd, msg = {}): from requests import get flask = 'http://127.0.0.1:1312/' r = get(flask + cmd, params=msg) return r.status_code def motorrun(motor,angle): import RPi.GPIO as GPIO from time import sleep from math import cos GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) spr = load_int(motor + '_stepsperrotation') dirpin = load_int('pin_' + motor + '_dir') steppin = load_int('pin_' + motor +'_step') dir = load_int(motor + '_dir') ramp = load_int(motor + '_accramp') acc = load_float(motor + '_acc') delay_init = load_float(motor + '_delay') delay = delay_init step_count=int(angle*spr/360) * dir GPIO.setup(dirpin, GPIO.OUT) GPIO.setup(steppin, GPIO.OUT) if (step_count>0): GPIO.output(dirpin, GPIO.HIGH) if(step_count<0): GPIO.output(dirpin, GPIO.LOW) step_count=-step_count for x in range(step_count): GPIO.output(steppin, GPIO.HIGH) if x<=ramp and x<=step_count/2: delay = delay_init * (1 + -1/acc*cos(1*(ramp-x)/ramp)+1/acc) #delay=delay_init+(ramp-x)*(delay_init)/acc elif step_count-x<=ramp and x>step_count/2: delay = delay_init * (1-1/acc*cos(1*(ramp+x-step_count)/ramp)+1/acc) #delay=delay_init+(ramp-step_count+x)*(delay_init)/acc else: delay = delay_init sleep(delay) GPIO.output(steppin, GPIO.LOW) sleep(delay) def ringlight(number,state): import RPi.GPIO as GPIO pin = load_int('pin_ringlight' + str(number)) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, state) def take_photo(file): from os import system filepath = basepath + file model=load_str('model') shutter = str(load_int('cam_shutter')) saturation = load_str('cam_saturation') contrast = load_str('cam_contrast') awbg_red = load_str('cam_awbg_red') awbg_blue = load_str('cam_awbg_blue') gain = load_str('cam_gain') quality = load_int('cam_jpeg_quality') filepath2 = '/home/pi/OpenScan/tmp/tmp.jpg' #width = load_str('cam_resx') #height = load_str('cam_resy') timeout = load_str('cam_timeout') cropx = load_int('cam_cropx')/200 cropy = load_int('cam_cropy')/200 rotation = load_int('cam_rotation') AF = load_bool('cam_AFmode') camera = load_str('camera') if camera == 'imx519' and AF == True: autofocus = ' --autofocus ' else: autofocus = '' cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus + ' >/dev/null 2>&1' # cmd = 'libcamera-still -n --denoise off --sharpness 0 -o ' + filepath2 + ' -t ' + timeout +' --shutter ' + shutter + ' --saturation ' + saturation + ' --contrast ' + contrast + ' --awbgains '+awbg_red + "," + awbg_blue + ' --gain ' + gain + ' -q ' + str(quality) + autofocus system(cmd) return cmd def get_points(samples=1): from math import pi, sqrt, acos, atan2, cos, sin points = [] phi = pi * (3. - sqrt(5.)) for i in range(int(samples)): y = 1 - (i / float(samples - 1)) * 2 radius = sqrt(1 - y * y) theta = phi * i x = cos(theta) * radius z = sin(theta) * radius r=sqrt(x*x+y*y+z*z) theta_neu=acos(z/r)*180/pi phi_neu=atan2(y,x)*180/pi points.append((theta_neu-90,phi_neu)) points.sort() return points def create_coordinates(angle_min, angle_max,point_count): point_count_final=point_count if angle_max < angle_min: a = angle_min angle_min = angle_max angle_max = a point_count=point_count*90/(angle_max-angle_min) actual_points=0 while actual_pointsangle_min and x20: point_count=point_count+3 else: point_count=point_count+1 return filtered ================================================ FILE: update/mini/config.txt ================================================ # For more options and information see # http://rpf.io/configtxt # Some settings may impact device functionality. See link above for details # uncomment if you get no picture on HDMI for a default "safe" mode #hdmi_safe=1 hdmi_blanking=2 # uncomment the following to adjust overscan. Use positive numbers if console # goes off screen, and negative if there is too much border #overscan_left=16 #overscan_right=16 #overscan_top=16 #overscan_bottom=16 # uncomment to force a console size. By default it will be display's size minus # overscan. #framebuffer_width=1280 #framebuffer_height=720 # uncomment if hdmi display is not detected and composite is being output #hdmi_force_hotplug=1 # uncomment to force a specific HDMI mode (this will force VGA) #hdmi_group=1 #hdmi_mode=1 # uncomment to force a HDMI mode rather than DVI. This can make audio work in # DMT (computer monitor) modes #hdmi_drive=2 # uncomment to increase signal to HDMI, if you have interference, blanking, or # no display #config_hdmi_boost=4 # uncomment for composite PAL #sdtv_mode=2 #uncomment to overclock the arm. 700 MHz is the default. #arm_freq=800 # Uncomment some or all of these to enable the optional hardware interfaces #dtparam=i2c_arm=on #dtparam=i2s=on #dtparam=spi=on # Uncomment this to enable infrared communication. #dtoverlay=gpio-ir,gpio_pin=17 #dtoverlay=gpio-ir-tx,gpio_pin=18 # Additional overlays and parameters are documented /boot/overlays/README # Enable audio (loads snd_bcm2835) dtparam=audio=on # Automatically load overlays for detected cameras camera_auto_detect=0 # Automatically load overlays for detected DSI displays display_auto_detect=1 # Enable DRM VC4 V3D driver #dtoverlay=vc4-kms-v3d max_framebuffers=2 # Disable compensation for displays with overscan disable_overscan=1 [cm4] # Enable host mode on the 2711 built-in XHCI USB controller. # This line should be removed if the legacy DWC2 controller is required # (e.g. for USB device mode) or if USB support is not required. otg_mode=1 [pi4] # Run as fast as firmware / board allows arm_boost=1 [all] camera_auto_detect=0 gpu_mem=256 dtoverlay=vc4-fkms-v3d dtoverlay=imx519,media-controller=0 dtoverlay=imx519 ================================================ FILE: update/mini/fla.py ================================================ from flask import Flask, make_response, jsonify, request, abort from PIL import Image import gphoto2 as gp from time import sleep import shutil from OpenScan import load_int, load_float, load_bool import RPi.GPIO as GPIO GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) app = Flask(__name__) basedir = '/home/pi/OpenScan/' ################################################################################################################### @app.route('/gphoto_init', methods=['get']) def gphoto_init(): global camera camera = gp.Camera() camera.init() return ({}, 200) ################################################################################################################### @app.route('/gphoto_preview', methods=['get']) def gphoto_preview(): filepath = str(request.args.get('filepath')) camera_file = gp.gp_camera_capture_preview(camera)[1] target = basedir + filepath camera_file.save(target) return ({}, 200) ################################################################################################################### @app.route('/gphoto_capture', methods=['get']) def gphoto_capture(): filepath = str(request.args.get('filepath')) file_path = camera.capture(gp.GP_CAPTURE_IMAGE) camera_file = camera.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL) camera_file.save(basedir + filepath) return ({}, 200) ################################################################################################################### @app.route('/gphoto_test', methods=['get']) def gphoto_test(): text = camera.get_summary() return ({}, 200) ################################################################################################################### @app.route('/gphoto_exit', methods=['get']) def gphoto_exit(): global camera camera.exit() return ({}, 200) ################################################################################################################### @app.route('/crop', methods=['get']) def crop(): downscale_threshold = 1000 filepath_in = basedir + str(request.args.get('filepath_in')) filepath_out = basedir + str(request.args.get('filepath_out')) cropx = int(request.args.get('cropx'))/200 cropy = int(request.args.get('cropy'))/200 rotation = int(request.args.get('rotation')) preview = str(request.args.get('preview')) with Image.open(filepath_in) as img: w,h = img.size if cropx != 0 or cropy != 0: img = img.crop((w*cropx, h*cropy, w * (1-cropx), h * (1-cropy))) if rotation == 90: img = img.transpose(Image.ROTATE_90) elif rotation == 180: img= img.transpose(Image.ROTATE_180) elif rotation == 270: img= img.transpose(Image.ROTATE_270) if preview == "True": w,h = img.size if w > downscale_threshold or h > downscale_threshold: downscale = max(w/downscale_threshold,h/downscale_threshold) img = img.resize((int(w/downscale),int(h/downscale)),Image.ANTIALIAS) img.save(filepath_out, quality=95, subsampling=0) return ({}, 200) ################################################################################################################### @app.route('/external_capture', methods=['get']) def external_capture(): pin = load_int('pin_external') delay_before = load_float('cam_delay_before') timeout = load_float('cam_timeout')/1000 delay_after = load_float('cam_delay_after') GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, GPIO.LOW) sleep(delay_before) GPIO.output(pin, GPIO.HIGH) sleep(timeout) GPIO.output(pin, GPIO.LOW) sleep(delay_after) return ({}, 200) if __name__ == '__main__': app.run(host='127.0.0.1', port=1312, debug=False, threaded=True) # app.run(host='0.0.0.0', port=1312, debug=False, threaded=True) ================================================ FILE: update/mini/flows.json ================================================ [ { "id": "829d803b6033a693", "type": "tab", "label": "HOME", "disabled": false, "info": "", "env": [] }, { "id": "1613373abaf77a2c", "type": "tab", "label": "SCAN", "disabled": false, "info": "", "env": [] }, { "id": "4981d84ef1a366d1", "type": "tab", "label": "Files&Cloud", "disabled": false, "info": "", "env": [] }, { "id": "017bd4e4a428bee5", "type": "tab", "label": "SETTINGS", "disabled": false, "info": "", "env": [] }, { "id": "b3150b13e34b1fe8", "type": "ui_tab", "name": "OpenScan", "icon": "dashboard", "order": 1, "disabled": false, "hidden": false }, { "id": "b6e9c2df6b28ff66", "type": "ui_base", "theme": { "name": "theme-dark", "lightTheme": { "default": "#0094CE", "baseColor": "#0094CE", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "darkTheme": { "default": "#097479", "baseColor": "#097479", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", "edited": true, "reset": false }, "customTheme": { "name": "Untitled Theme 1", "default": "#4B7930", "baseColor": "#4B7930", "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" }, "themeState": { "base-color": { "default": "#097479", "value": "#097479", "edited": false }, "page-titlebar-backgroundColor": { "value": "#097479", "edited": false }, "page-backgroundColor": { "value": "#111111", "edited": false }, "page-sidebar-backgroundColor": { "value": "#333333", "edited": false }, "group-textColor": { "value": "#0eb8c0", "edited": false }, "group-borderColor": { "value": "#555555", "edited": false }, "group-backgroundColor": { "value": "#333333", "edited": false }, "widget-textColor": { "value": "#eeeeee", "edited": false }, "widget-backgroundColor": { "value": "#097479", "edited": false }, "widget-borderColor": { "value": "#333333", "edited": false }, "base-font": { "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" } }, "angularTheme": { "primary": "indigo", "accents": "blue", "warn": "red", "background": "grey", "palette": "light" } }, "site": { "name": "OpenScan 3D Scanner", "hideToolbar": "false", "allowSwipe": "false", "lockMenu": "false", "allowTempTheme": "true", "dateFormat": "DD/MM/YYYY", "sizes": { "sx": 46, "sy": 46, "gx": 10, "gy": 10, "cx": 6, "cy": 6, "px": 6, "py": 6 } } }, { "id": "729f9ea6e3513c9b", "type": "ui_group", "name": "Home", "tab": "b3150b13e34b1fe8", "order": 1, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "65ae49b64fa0d83e", "type": "ui_tab", "name": "Settings", "icon": "dashboard", "order": 4, "disabled": false, "hidden": false }, { "id": "e23b837a9f040895", "type": "ui_tab", "name": "Scan", "icon": "dashboard", "order": 2, "disabled": false, "hidden": false }, { "id": "7aaf184330605300", "type": "ui_group", "name": "Settings", "tab": "e23b837a9f040895", "order": 1, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "90223f7ddc082321", "type": "ui_group", "name": "Arducam", "tab": "e23b837a9f040895", "order": 3, "disp": false, "width": 10, "collapse": false, "className": "" }, { "id": "7625f9c9e8dbc5c6", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "", "order": 4, "width": 1, "height": 1 }, { "id": "3b4bd36726be16d5", "type": "ui_group", "name": "Settings", "tab": "65ae49b64fa0d83e", "order": 3, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "b5fdd57b.15eda8", "type": "ui_group", "name": "Main", "tab": "15a222ed.d70a7d", "order": 1, "disp": false, "width": 13, "collapse": false }, { "id": "db43d646.2074c8", "type": "ui_group", "name": "OpenScanCloud", "tab": "15a222ed.d70a7d", "order": 2, "disp": false, "width": "6", "collapse": false, "className": "" }, { "id": "15a222ed.d70a7d", "type": "ui_tab", "name": "Files&Cloud", "icon": "dashboard", "order": 3, "disabled": false, "hidden": false }, { "id": "d9b28c5fbfd37509", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "7aaf184330605300", "order": 21, "width": 1, "height": 1 }, { "id": "cc7d61f1cfd44578", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "7aaf184330605300", "order": 24, "width": 1, "height": 1 }, { "id": "bba223eda03b67eb", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "7aaf184330605300", "order": 25, "width": 1, "height": 1 }, { "id": "e28012f571abd8dc", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "7aaf184330605300", "order": 28, "width": 1, "height": 1 }, { "id": "b32d71fea63612b7", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "90223f7ddc082321", "order": 1, "width": 2, "height": 1 }, { "id": "4cf8566a9121fe3f", "type": "ui_spacer", "z": "1613373abaf77a2c", "name": "spacer", "group": "90223f7ddc082321", "order": 7, "width": 2, "height": 1 }, { "id": "2cc829a5.610f36", "type": "ui_group", "name": "group-test", "tab": "", "order": 3, "disp": false, "width": 3, "collapse": false }, { "id": "65285822f24120f4", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "3b4bd36726be16d5", "order": 8, "width": 6, "height": 1 }, { "id": "90d444dfdfccf138", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "3b4bd36726be16d5", "order": 15, "width": 6, "height": 1 }, { "id": "c9dbf6219837d6d7", "type": "ui_spacer", "z": "017bd4e4a428bee5", "name": "spacer", "group": "3b4bd36726be16d5", "order": 22, "width": 6, "height": 1 }, { "id": "3fe52603e2ac73b6", "type": "ui_template", "z": "829d803b6033a693", "group": "729f9ea6e3513c9b", "name": "Background", "order": 1, "width": 0, "height": 0, "format": "", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "global", "className": "", "x": 110, "y": 40, "wires": [ [] ] }, { "id": "4468f691.103eb8", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 2, "width": 2, "height": 2, "passthru": false, "label": "SCAN", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "1", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 100, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "6560dd25.9e76c4", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 4, "width": 2, "height": 2, "passthru": false, "label": "Settings", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "3", "payloadType": "num", "topic": "", "topicType": "str", "x": 100, "y": 180, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "62cd5288.2805fc", "type": "ui_ui_control", "z": "829d803b6033a693", "name": "", "events": "all", "x": 300, "y": 100, "wires": [ [] ] }, { "id": "71e72293.91c6fc", "type": "ui_button", "z": "829d803b6033a693", "name": "", "group": "729f9ea6e3513c9b", "order": 3, "width": 2, "height": 2, "passthru": false, "label": "Files", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "2", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 140, "wires": [ [ "62cd5288.2805fc" ] ] }, { "id": "88edad7ca53698fd", "type": "inject", "z": "829d803b6033a693", "name": "1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "true", "payloadType": "bool", "x": 90, "y": 320, "wires": [ [ "000a811a215e08d4", "83c2b5ea51f0fec3", "88fde4ab78c965d7", "bee62d2a99cbc63b", "4fa53fb9738cb1d3" ] ] }, { "id": "bd75f33b8a57c522", "type": "link out", "z": "829d803b6033a693", "name": "enable", "mode": "link", "links": [ "8367cfa0bf5bc5df", "92c98e6ce7cd25f9" ], "x": 335, "y": 360, "wires": [] }, { "id": "000a811a215e08d4", "type": "function", "z": "829d803b6033a693", "name": "enable", "func": "msg.enabled = true\nmsg.payload = 1\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 360, "wires": [ [ "bd75f33b8a57c522" ] ] }, { "id": "83c2b5ea51f0fec3", "type": "function", "z": "829d803b6033a693", "name": "disable", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 240, "y": 400, "wires": [ [ "6b94bf2295b1b31d" ] ] }, { "id": "6b94bf2295b1b31d", "type": "link out", "z": "829d803b6033a693", "name": "disable", "mode": "link", "links": [ "a1d29e56599da0bd" ], "x": 335, "y": 400, "wires": [] }, { "id": "88fde4ab78c965d7", "type": "function", "z": "829d803b6033a693", "name": "write", "func": "var file = 'status_cloud'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\ncontent = 'ready'\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 440, "wires": [ [] ] }, { "id": "960912e90ba5b5bc", "type": "link out", "z": "829d803b6033a693", "name": "started1s", "mode": "link", "links": [ "2f4c0f98.dee2", "397ab7f44b893c89", "65145c939b6647e2", "65b38bfeb3fee710", "6d1e12f51f9af0b6", "788fabff98c7973c", "9b2bc9849aee310b", "a1e14624058e74cd", "a67c18aaca2f5fa5", "bd80ec228fb9a86d", "cc9c4092edeb43cc", "d3fc91d87d5d5f62", "d7c1fb4c028b21a5", "e5f38b4a07a5e278", "f0b355967b33dfee", "d0104e0163745993", "5e7d5e4335d37794", "e0965e490d53617f", "612a7556ab11cf7d", "482bc06e02eec5b9" ], "x": 615, "y": 720, "wires": [] }, { "id": "168d72a54504b327", "type": "inject", "z": "829d803b6033a693", "name": "5/0.1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.1", "crontab": "", "once": true, "onceDelay": "5", "topic": "", "payload": "", "payloadType": "str", "x": 100, "y": 620, "wires": [ [ "6c6ef2255a7d39e5" ] ] }, { "id": "6c6ef2255a7d39e5", "type": "link out", "z": "829d803b6033a693", "name": "repeat 5s/0.1s", "mode": "link", "links": [ "61990987acd0f263", "2415272f42ce468c" ], "x": 195, "y": 640, "wires": [] }, { "id": "bee62d2a99cbc63b", "type": "function", "z": "829d803b6033a693", "name": "global", "func": "global.set('flag_pw', true)\nglobal.set('flag', true)\nglobal.set('combine', false)\nglobal.set('focus', 2838)\nglobal.set('focus1', 0)\nglobal.set('focus2', 0)\n\nglobal.set('focuser', true)\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 230, "y": 320, "wires": [ [] ] }, { "id": "544d20f02215011a", "type": "function", "z": "829d803b6033a693", "name": "CREATE FACTORY DEFAULT", "func": "msg = { \n'overwrite':msg.overwrite,\n'settings':\n {\n 'advanced_settings':false,\n 'cameras':{\n 'imx519':[4656,3496],\n 'imx219':[3280,2464],\n 'imx477':[4056,3040],\n 'ov5647':[2592,1944],\n 'imx378':[3840,2880],\n 'ov9271':[1280,800],\n 'imx290a':[1920,1080],\n 'imx290b':[1920,1080],\n },\n 'cam_AFmode':true,\n 'cam_STmode':true,\n 'cam_stacksize':2,\n 'cam_awbg_blue':0,\n 'cam_awbg_red':0,\n 'cam_contrast':1,\n 'cam_cropx':0,\n 'cam_cropy':0,\n 'cam_delay_after':0,\n 'cam_delay_before':0,\n 'camera':'',\n 'cam_gain':1,\n 'cam_jpeg_quality':95,\n 'cam_rotation':0,\n 'cam_saturation':1,\n 'cam_shutter':50000,\n 'cam_timeout':800,\n 'hostname':'',\n 'model':'',\n 'osc_credit':'',\n 'osc_limit_filesize':'',\n 'osc_limit_photos':'',\n 'osc_splitsize':200000000,\n 'pin_endstop1':24,\n 'pin_endstop2':25,\n 'pin_external':10,\n 'pin_ringlight1':17,\n 'pin_ringlight2':27,\n 'pin_rotor_dir':5,\n 'pin_rotor_enable':23,\n 'pin_rotor_step':6,\n 'pin_tt_dir':9,\n 'pin_tt_enable':22,\n 'pin_tt_step':11,\n 'rotor_acc':1,\n 'rotor_accramp':2000,\n 'rotor_angle':10,\n 'rotor_anglemax':60,\n 'rotor_anglemin':-40,\n 'rotor_anglestart':0,\n 'rotor_delay':0.0001,\n 'rotor_dir':1,\n 'rotor_stepsperrotation':48000,\n 'routine_photocount':50,\n 'routine_projectname':'default',\n 'smb':true,\n 'ssh':true,\n 'status_cloud':'ready',\n 'status_internal_cam':'--READY--',\n 'status_uploadprogress':'',\n 'terms':false,\n 'token':'',\n 'tt_acc':1,\n 'tt_accramp':200,\n 'tt_angle':10,\n 'tt_delay':0.0001,\n 'tt_dir':1,\n 'tt_stepsperrotation':3200,\n 'cam_focus':2838,\n 'cam_focus1':0,\n 'cam_focus2':0,\n 'uploadprogress':'',\n 'update_type':'main',\n 'update_auto':true,\n 'turntable_mode':true,\n}}\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 310, "y": 720, "wires": [ [ "c77552216a8bb781" ] ] }, { "id": "a1f0ed7d5a9d670e", "type": "inject", "z": "829d803b6033a693", "name": "", "props": [ { "p": "overwrite", "v": "false", "vt": "bool" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "0.1", "topic": "", "x": 90, "y": 720, "wires": [ [ "544d20f02215011a" ] ] }, { "id": "c77552216a8bb781", "type": "python3-function", "z": "829d803b6033a693", "name": "chk files", "func": "from os import listdir\n\nbasedir = '/home/pi/OpenScan/settings/'\n\nmsg['payload'] = ''\n\nfiles = listdir(basedir)\n\nfor i in msg['settings']:\n if msg['overwrite'] != True:\n if i not in files:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n else:\n with open(basedir + i, 'w+') as file:\n file.write(str(msg['settings'][i])) \n\nmsg['payload'] = True\n\nreturn msg", "outputs": 1, "x": 520, "y": 720, "wires": [ [ "960912e90ba5b5bc" ] ] }, { "id": "38783aea9cc317a6", "type": "link in", "z": "829d803b6033a693", "name": "factory reset", "links": [ "80bccc884b0be297" ], "x": 135, "y": 760, "wires": [ [ "544d20f02215011a" ] ] }, { "id": "4fa53fb9738cb1d3", "type": "python3-function", "z": "829d803b6033a693", "name": "create log", "func": "import subprocess\n\n\nlog = '############################################DMESG############################################\\n'\nlog += subprocess.getoutput(\"dmesg\")\nlog += '\\n############################################SYSLOG############################################\\n'\nlog += subprocess.getoutput(\"tail -10000 /var/log/syslog\")\n\nwith open('/home/pi/OpenScan/tmp/log.txt', 'w+') as file:\n file.write(log)\n\nreturn msg", "outputs": 1, "x": 240, "y": 480, "wires": [ [] ] }, { "id": "828e5298.d2192", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 9, "width": 2, "height": 1, "passthru": false, "label": "⇐", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 400, "wires": [ [ "b12e54fb.3141b8" ] ] }, { "id": "96c7e241.458e6", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 10, "width": 2, "height": 1, "passthru": false, "label": "⇒", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 440, "wires": [ [ "37f52dd4.bd7572" ] ] }, { "id": "2e854876.6b6008", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 6, "width": 2, "height": 1, "passthru": true, "label": "⇑", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 280, "wires": [ [ "555aea34.b3b5e4" ] ] }, { "id": "753817f.1b9b3e8", "type": "ui_button", "z": "1613373abaf77a2c", "name": "", "group": "7aaf184330605300", "order": 7, "width": 2, "height": 1, "passthru": true, "label": "⇓", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "num", "topic": "", "topicType": "str", "x": 90, "y": 320, "wires": [ [ "9905e0c9.dddcd" ] ] }, { "id": "8775044.3aa3ef8", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 8, "width": 2, "height": 1, "name": "", "label": "Turntable", "format": "", "layout": "row-left", "className": "", "x": 100, "y": 360, "wires": [] }, { "id": "9e8a2d23.bf6ce", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 5, "width": 2, "height": 1, "name": "", "label": "Rotor", "format": "", "layout": "row-left", "className": "", "x": 90, "y": 240, "wires": [] }, { "id": "555aea34.b3b5e4", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 280, "wires": [ [ "46e00b45.c24ca4" ] ] }, { "id": "9905e0c9.dddcd", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 320, "wires": [ [ "6ee089cb343a35ef" ] ] }, { "id": "b12e54fb.3141b8", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 400, "wires": [ [ "c1871a2b9af5419a" ] ] }, { "id": "37f52dd4.bd7572", "type": "delay", "z": "1613373abaf77a2c", "name": "lmt 0.2/s", "pauseType": "rate", "timeout": "0.1", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "0.2", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "outputs": 1, "x": 220, "y": 440, "wires": [ [ "42b9f1fc49e69f54" ] ] }, { "id": "46e00b45.c24ca4", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Rotor left", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('rotor',load_int('rotor_angle'))", "outputs": 1, "x": 360, "y": 280, "wires": [ [] ] }, { "id": "6ee089cb343a35ef", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Rotor right", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('rotor',-load_int('rotor_angle'))", "outputs": 1, "x": 370, "y": 320, "wires": [ [] ] }, { "id": "42b9f1fc49e69f54", "type": "python3-function", "z": "1613373abaf77a2c", "name": "TT right", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('tt',-load_int('tt_angle'))", "outputs": 1, "x": 360, "y": 440, "wires": [ [] ] }, { "id": "c1871a2b9af5419a", "type": "python3-function", "z": "1613373abaf77a2c", "name": "TT left", "func": "from OpenScan import motorrun, load_int\n\nmotorrun('tt',load_int('tt_angle'))", "outputs": 1, "x": 350, "y": 400, "wires": [ [] ] }, { "id": "aebad788761dce4a", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "routine_photocount", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 14, "width": 3, "height": 1, "passthru": true, "outs": "end", "topic": "", "topicType": "str", "min": "10", "max": "300", "step": "10", "className": "", "x": 350, "y": 540, "wires": [ [ "ce28a0b5bfb0d5a1" ] ] }, { "id": "107a030938cbfea9", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 540, "wires": [ [ "aebad788761dce4a" ] ] }, { "id": "ce28a0b5bfb0d5a1", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'routine_photocount'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 540, "wires": [ [] ] }, { "id": "84d6b96c8ebaac96", "type": "function", "z": "1613373abaf77a2c", "name": "loadF", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseFloat(data) / 1000;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 580, "wires": [ [ "470b10726d298834" ] ] }, { "id": "470b10726d298834", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "shutter ", "label": " ", "tooltip": "", "group": "7aaf184330605300", "order": 16, "width": 3, "height": 1, "passthru": true, "outs": "end", "topic": "", "topicType": "str", "min": "1", "max": "300", "step": "1", "className": "", "x": 310, "y": 580, "wires": [ [ "44c3947a9b92d32d" ] ] }, { "id": "44c3947a9b92d32d", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_shutter'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload * 1000)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 580, "wires": [ [] ] }, { "id": "069bcf58b1fe44cd", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 13, "width": 3, "height": 1, "name": "photocount", "label": "Photos", "format": "", "layout": "row-left", "className": "", "x": 670, "y": 540, "wires": [] }, { "id": "8dc7df1de59cb03a", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 15, "width": 3, "height": 1, "name": "shutter", "label": "Shutter (ms)", "format": "", "layout": "row-left", "className": "", "x": 650, "y": 580, "wires": [] }, { "id": "cc69dba8d54a29dd", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "Crop X", "label": " ", "tooltip": "", "group": "7aaf184330605300", "order": 18, "width": 3, "height": 1, "passthru": true, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "99", "step": "1", "className": "", "x": 320, "y": 620, "wires": [ [ "c2b2ab5524271123" ] ] }, { "id": "e3a90602605fb9e9", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "Crop Y", "label": " ", "tooltip": "", "group": "7aaf184330605300", "order": 20, "width": 3, "height": 1, "passthru": true, "outs": "end", "topic": "", "topicType": "str", "min": "0", "max": "99", "step": "1", "className": "", "x": 310, "y": 660, "wires": [ [ "26f17a7f406df73c" ] ] }, { "id": "9c6b48b7b4cc4e1a", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 620, "wires": [ [ "cc69dba8d54a29dd" ] ] }, { "id": "c470fd0b15356206", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "var file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = parseInt(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 660, "wires": [ [ "e3a90602605fb9e9" ] ] }, { "id": "c2b2ab5524271123", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_cropx'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 620, "wires": [ [] ] }, { "id": "26f17a7f406df73c", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'cam_cropy'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 660, "wires": [ [] ] }, { "id": "fecf5cff888bb570", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 17, "width": 3, "height": 1, "name": "cropx", "label": "Crop X (%)", "format": "", "layout": "row-left", "className": "", "x": 650, "y": 620, "wires": [] }, { "id": "0ee4950bd21498bd", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 19, "width": 3, "height": 1, "name": "cropy", "label": "Crop Y (%)", "format": "", "layout": "row-left", "className": "", "x": 650, "y": 660, "wires": [] }, { "id": "ebbf11b55d758806", "type": "ui_text_input", "z": "1613373abaf77a2c", "name": "", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 4, "width": 3, "height": 1, "passthru": true, "mode": "text", "delay": "0", "topic": "", "sendOnBlur": true, "className": "", "topicType": "str", "x": 320, "y": 500, "wires": [ [ "67385b196c517ac6" ] ] }, { "id": "f4b3112a9ec6c487", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.payload=\"default\"\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 500, "wires": [ [ "ebbf11b55d758806" ] ] }, { "id": "67385b196c517ac6", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'routine_projectname'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\nconst content = String(msg.payload).replace(/ /g, '_')\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 510, "y": 500, "wires": [ [] ] }, { "id": "4dd7285c2b0fd79b", "type": "ui_slider", "z": "1613373abaf77a2c", "name": "ringlight", "label": "", "tooltip": "", "group": "7aaf184330605300", "order": 12, "width": 3, "height": 1, "passthru": true, "outs": "all", "topic": "", "topicType": "str", "min": 0, "max": "3", "step": 1, "className": "", "x": 320, "y": 700, "wires": [ [ "873dace18a23fdf2" ] ] }, { "id": "873dace18a23fdf2", "type": "python3-function", "z": "1613373abaf77a2c", "name": "LED", "func": "from OpenScan import ringlight\nval = msg['payload']\n\nif val == 0:\n ringlight(1,False)\n ringlight(2,False)\nelif val == 1:\n ringlight(1,True)\n ringlight(2,False)\nelif val == 2:\n ringlight(1,False)\n ringlight(2,True)\nelif val == 3:\n ringlight(1,True)\n ringlight(2,True)\n", "outputs": 1, "x": 510, "y": 700, "wires": [ [] ] }, { "id": "9e30e33a1520fee0", "type": "function", "z": "1613373abaf77a2c", "name": "loadI", "func": "msg.payload = 0\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 190, "y": 700, "wires": [ [ "4dd7285c2b0fd79b" ] ] }, { "id": "7dd287f40385922f", "type": "ui_button", "z": "1613373abaf77a2c", "name": "start ", "group": "7aaf184330605300", "order": 22, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-play", "payload": "", "payloadType": "date", "topic": "enabled", "topicType": "str", "x": 150, "y": 880, "wires": [ [ "431f917c2541ae48", "33d94a04b96a2de0", "6d15f717d5a11002" ] ] }, { "id": "579f2211199fd6ab", "type": "ui_button", "z": "1613373abaf77a2c", "name": "stop", "group": "7aaf184330605300", "order": 23, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "fa-stop", "payload": "numberofphotos", "payloadType": "global", "topic": "", "topicType": "str", "x": 750, "y": 920, "wires": [ [ "1787f08ed7070ddd", "c1c044f3c2139f68" ] ] }, { "id": "431f917c2541ae48", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Routine", "func": "from OpenScan import load_str, load_int, motorrun, create_coordinates, take_photo, save, load_bool, camera\nfrom time import sleep, strftime\nfrom zipfile import ZipFile, ZIP_DEFLATED\nfrom os import system\nfrom os.path import isfile\nfrom Arducam import Focuser\n\nstatus = load_str(\"status_internal_cam\")\n\nif status == \"no camera found\":\n return\n\nif not status == \"Routine-stopping\":\n save('status_internal_cam','Routine-preparing')\n\nprojectname=load_str(\"routine_projectname\")\nphotocount = load_int('routine_photocount') #vorher point_count\nangle_max = load_int('rotor_anglemax')\nangle_min = load_int('rotor_anglemin')\nangle_start = load_int('rotor_anglestart')\n\ncounter = 0\nbasepath = '/home/pi/OpenScan/'\ntemppath = basepath + 'tmp/tmp.jpg'\nzippath = basepath + 'tmp/tmp.zip'\n\nif not 'projectcode' in msg:\n projectcode = strftime('20%y-%m-%d_%H.%M.%S-') + projectname\n msg['projectcode'] = projectcode\n msg['counter'] = -1\n if isfile(zippath):\n system('rm ' + zippath)\n sleep(1)\n\nprojectcode = msg['projectcode']\nmsg['counter'] += 1\n\ntt_mode = load_bool('turntable_mode')\n\nif tt_mode == False:\n coordinates = create_coordinates(angle_min,angle_max,photocount)\nelse:\n angle_start = 0\n coordinates = []\n for i in range (photocount):\n coordinates.append([0,360/photocount*(i+1)])\n\nposition_last = (angle_start , 0)\n\nzip = ZipFile(zippath, \"a\",ZIP_DEFLATED, allowZip64=True)\n\nfor position in coordinates:\n counter += 1\n if load_str('status_internal_cam') == \"Routine-stopping\":\n break\n\n\n rotor_angle = position_last[0] - position[0]\n if abs(rotor_angle) > 180:\n rotor_angle = -360 * rotor_angle/abs(rotor_angle) + rotor_angle\n\n tt_angle = position_last[1] - position[1]\n #if abs(tt_angle) > 180:\n # tt_angle = -360 * tt_angle/abs(tt_angle) + tt_angle\n \n motorrun('rotor', rotor_angle)\n motorrun('tt', tt_angle)\n\n\n\n msg['cropx'] = load_int('cam_cropx')\n msg['cropy'] = load_int('cam_cropy')\n msg['rotation'] = load_int('cam_rotation')\n msg['filepath_in'] = 'tmp/tmp.jpg'\n msg['filepath_out'] = 'tmp/tmp.jpg'\n msg['filepath'] = 'tmp/tmp.jpg'\n if load_str('status_internal_cam') != \"Routine-stopping\":\n save('status_internal_cam','Routine-Photo ' + str(counter) + '/' + str(photocount))\n take_photo('tmp/tmp.jpg')\n camera('/crop',msg)\n\n\n \n zip.write(temppath, projectname + '_' + str(counter + msg['counter']*photocount) + \".jpg\")\n system('cp ' + temppath + ' ' + basepath +'tmp/preview.jpg')\n\n position_last = position\n\n\nzip.close()\n\n\nsave('status_internal_cam','Routine-done')\n\nmotorrun('rotor',position_last[0] - angle_start)\nmotorrun('tt',position_last[1])\n\nsave('status_internal_cam','--READY--')\n\nmsg['topic'] = 'Scan done'\nmsg['payload'] = 'Do you want to continue scanning or finish this project?'\nmsg['enabled'] = False\nreturn msg\n\n", "outputs": 1, "x": 300, "y": 840, "wires": [ [ "db7eea74d3bf892b" ] ] }, { "id": "1787f08ed7070ddd", "type": "python3-function", "z": "1613373abaf77a2c", "name": "stop", "func": "from OpenScan import load_str, save\n\nstatus = load_str('status_internal_cam')\n\nif status == 'no camera found' or status[:5]=='Featu' or status =='--READY--':\n return\n\nsave('status_internal_cam', 'Routine-stopping')", "outputs": 1, "x": 930, "y": 920, "wires": [ [] ] }, { "id": "e9b13dfd9f8d3711", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6" ], "x": 395, "y": 800, "wires": [] }, { "id": "5ba05110851a5096", "type": "ui_button", "z": "1613373abaf77a2c", "name": "reboot", "group": "7aaf184330605300", "order": 26, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "reboot", "color": "", "bgcolor": "", "className": "", "icon": "fa-repeat", "payload": "", "payloadType": "date", "topic": "", "topicType": "str", "x": 90, "y": 1000, "wires": [ [ "16c76929f88df841" ] ] }, { "id": "152d402caa595189", "type": "ui_button", "z": "1613373abaf77a2c", "name": "shutdown", "group": "7aaf184330605300", "order": 27, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "shutdown", "color": "", "bgcolor": "", "className": "", "icon": "fa-power-off", "payload": "", "payloadType": "date", "topic": "", "topicType": "str", "x": 100, "y": 1040, "wires": [ [ "597bfb653e8cddbf" ] ] }, { "id": "16c76929f88df841", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "9bb0adbd716ce347" ], "x": 215, "y": 1000, "wires": [] }, { "id": "597bfb653e8cddbf", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "fc9abb94c35eec56" ], "x": 215, "y": 1040, "wires": [] }, { "id": "9654deebb668e012", "type": "inject", "z": "1613373abaf77a2c", "name": "1s", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": "1", "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 960, "wires": [ [ "c1c044f3c2139f68" ] ] }, { "id": "8367cfa0bf5bc5df", "type": "link in", "z": "1613373abaf77a2c", "name": "start routine", "links": [ "210ef5246d1a8790", "84608db962fd9932", "8689e938.dd9e38", "f20f2dbc.0f123", "e9b13dfd9f8d3711", "96bdb9417e38810f", "fb13752beddee9f2", "bd75f33b8a57c522" ], "x": 55, "y": 880, "wires": [ [ "7dd287f40385922f" ] ] }, { "id": "fb13752beddee9f2", "type": "link out", "z": "1613373abaf77a2c", "name": "", "mode": "link", "links": [ "2f4c0f98.dee2", "8367cfa0bf5bc5df", "b33d604c.5f1a6", "482bc06e02eec5b9" ], "x": 865, "y": 880, "wires": [] }, { "id": "95439678bb2df2a2", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "msg.flag = global.get('flag')\nif (global.get('flag_pw')== true){\n return msg\n}\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 1160, "wires": [ [ "04cc2467807d2d6b", "14f9617b5b301318" ] ] }, { "id": "948a3ae4444685f2", "type": "change", "z": "1613373abaf77a2c", "name": "flag_pw true", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 650, "y": 1200, "wires": [ [] ] }, { "id": "04cc2467807d2d6b", "type": "change", "z": "1613373abaf77a2c", "name": "flag_pw false", "rules": [ { "t": "set", "p": "flag_pw", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 430, "y": 1200, "wires": [ [] ] }, { "id": "12f1399b240830bf", "type": "exec", "z": "1613373abaf77a2c", "command": " v4l2-ctl --list-formats-ext", "addpay": "", "append": "", "useSpawn": "true", "timer": "", "winHide": false, "oldrc": false, "name": "check cam", "x": 190, "y": 100, "wires": [ [ "6222f781629c72e7" ], [ "6222f781629c72e7" ], [] ] }, { "id": "6222f781629c72e7", "type": "function", "z": "1613373abaf77a2c", "name": "write", "func": "var file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\n\ncontent = '--READY--'\n\nif (msg.payload.includes('Cannot open device')){\n content = 'no camera found'\n}\n\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return msg\n }\n });\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 350, "y": 100, "wires": [ [] ] }, { "id": "e978bf8c53d1f15a", "type": "comment", "z": "1613373abaf77a2c", "name": "Settings internal cam", "info": "", "x": 120, "y": 40, "wires": [] }, { "id": "e9566588c5e40637", "type": "inject", "z": "1613373abaf77a2c", "name": "4s/0.5", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "0.5", "crontab": "", "once": true, "onceDelay": "10", "topic": "Repeat", "payload": "0.2", "payloadType": "str", "x": 120, "y": 1160, "wires": [ [ "95439678bb2df2a2" ] ] }, { "id": "14f9617b5b301318", "type": "python3-function", "z": "1613373abaf77a2c", "name": "Take Preview Shot", "func": "from time import time, sleep\nfrom OpenScan import load_str, load_bool, take_photo, camera, load_int,save\n\nstatus = load_str('status_internal_cam')\n\nif msg['flag'] == False and not 'Routine' in status:\n return\n\nmsg['payload']=\"/tmp/preview.jpg?ts=\"+str(int(time()*10))\n\nif status!=\"--READY--\" and status!=\"ERROR:flask\":\n return msg\n\nmsg['cropx'] = load_int('cam_cropx')\nmsg['cropy'] = load_int('cam_cropy')\nmsg['rotation'] = load_int('cam_rotation')\nmsg['filepath_in'] = 'tmp/tmp.jpg'\nmsg['filepath_out'] = 'tmp/preview.jpg'\nmsg['preview'] = True\n\ntake_photo('tmp/tmp.jpg')\n\ntry:\n camera('/crop',msg)\nexcept:\n save('status_internal_cam','ERROR: flask')\n pass\n \n\nreturn msg\n", "outputs": 1, "x": 450, "y": 1160, "wires": [ [ "948a3ae4444685f2", "8f5d87ce24c40b11" ] ] }, { "id": "1118d0965ff7c40b", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 3, "width": 3, "height": 1, "name": "projectname", "label": "Projectname", "format": "", "layout": "row-left", "className": "", "x": 670, "y": 500, "wires": [] }, { "id": "82c8ad50ecfbc755", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 11, "width": 3, "height": 1, "name": "ringlight", "label": "Ringlight", "format": "", "layout": "row-left", "className": "", "x": 660, "y": 700, "wires": [] }, { "id": "33d94a04b96a2de0", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "global.set('flag', false)\n\nvar file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\n\n\nif (data === 'no camera found'){\n return\n}\n\nmsg.enabled = true\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 920, "wires": [ [ "579f2211199fd6ab" ] ] }, { "id": "c1c044f3c2139f68", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 750, "y": 960, "wires": [ [ "579f2211199fd6ab" ] ] }, { "id": "9a368472a72fbc48", "type": "comment", "z": "1613373abaf77a2c", "name": "preview arducam with focus", "info": "", "x": 160, "y": 1100, "wires": [] }, { "id": "8f5d87ce24c40b11", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "preview_arducam", "order": 8, "width": 10, "height": 12, "format": "
\n\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 670, "y": 1160, "wires": [ [] ] }, { "id": "282efe64332193c8", "type": "python3-function", "z": "1613373abaf77a2c", "name": "focus", "func": "from OpenScan import load_str\n\nif load_str('camera') != 'imx519':\n return\n\nfrom Arducam import Focuser\n\n\nif msg['focuser'] == True:\n focuser = Focuser('/dev/v4l-subdev1')\n focuser.write(msg['focus'])\n return msg", "outputs": 1, "x": 1110, "y": 1320, "wires": [ [] ] }, { "id": "64b16ef47ab6d859", "type": "ui_switch", "z": "1613373abaf77a2c", "name": "MF", "label": "", "tooltip": "", "group": "90223f7ddc082321", "order": 3, "width": 1, "height": 1, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "false", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "true", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 150, "y": 1260, "wires": [ [ "f017f67a8d4a3750" ] ] }, { "id": "f017f67a8d4a3750", "type": "function", "z": "1613373abaf77a2c", "name": "enable", "func": "let fs = global.get('fs');\nfilepath = '/home/pi/OpenScan/settings/';\n\nvar file = 'status_internal_cam'\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data != '--READY--'){\n return\n}\n\nfile = 'cam_AFmode'\ncontent = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n});\n\nglobal.set('AF',msg.payload)\nmsg.enabled = false\nif (msg.payload == false){\n msg.enabled = true\n}\nif (msg.payload == true){\n file = 'cam_focus1'\n content = String(0)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n file = 'cam_focus2'\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n \n file = 'cam_stacksize'\n content = String(2)\n fs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });\n global.set('focus1', 0)\n global.set('focus2', 0)\n\n}\n\n\nmsg.focus = global.get('focus')\nmsg.payload = 'down'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 1260, "wires": [ [ "5c39bd09.702d84", "74521cf72050b515", "b70e8c24ee011258" ] ] }, { "id": "65145c939b6647e2", "type": "link in", "z": "1613373abaf77a2c", "name": "", "links": [ "960912e90ba5b5bc" ], "x": 55, "y": 1260, "wires": [ [ "64b16ef47ab6d859" ] ] }, { "id": "5ea18678.975138", "type": "trigger", "z": "1613373abaf77a2c", "name": "20ms", "op1": "", "op2": "0", "op1type": "pay", "op2type": "str", "duration": "-20", "extend": false, "overrideDelay": false, "units": "ms", "reset": "", "bytopic": "all", "topic": "topic", "outputs": 1, "x": 730, "y": 1300, "wires": [ [ "fd93843e238cc9ce" ] ] }, { "id": "5c39bd09.702d84", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "F+", "order": 4, "width": 1, "height": 1, "format": " ", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 410, "y": 1260, "wires": [ [ "dcfb5cce.0431a" ] ] }, { "id": "dcfb5cce.0431a", "type": "switch", "z": "1613373abaf77a2c", "name": "", "property": "payload", "propertyType": "msg", "rules": [ { "t": "eq", "v": "1", "vt": "num" }, { "t": "eq", "v": "-1", "vt": "num" }, { "t": "eq", "v": "up", "vt": "str" } ], "checkall": "true", "repair": false, "outputs": 3, "x": 550, "y": 1280, "wires": [ [ "5ea18678.975138", "f4a41b1e7b221486" ], [ "5ea18678.975138", "f4a41b1e7b221486" ], [ "8cdd0a6b.40bcd8" ] ] }, { "id": "8cdd0a6b.40bcd8", "type": "change", "z": "1613373abaf77a2c", "name": "", "rules": [ { "t": "set", "p": "reset", "pt": "msg", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 560, "y": 1340, "wires": [ [ "5ea18678.975138", "e9b3837b1ffb0360" ] ] }, { "id": "74521cf72050b515", "type": "ui_template", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "name": "F-", "order": 5, "width": 1, "height": 1, "format": " ", "storeOutMessages": true, "fwdInMessages": true, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 410, "y": 1300, "wires": [ [ "dcfb5cce.0431a" ] ] }, { "id": "7219f62c9fdc6753", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 6, "width": 2, "height": 1, "name": "", "label": "{{msg.payload}}", "format": "", "layout": "col-center", "className": "", "x": 1130, "y": 1280, "wires": [] }, { "id": "b70e8c24ee011258", "type": "function", "z": "1613373abaf77a2c", "name": "global", "func": "if (msg.payload == 'down'){\n msg.enabled = false\n msg.payload = ' '\n msg.focuser = global.get('focuser')\n return msg\n}\n\n\nmsg.enabled = true\n\nsign = msg.payload\nfocus = global.get('focus')\nif (focus > 3000){\n focusstep = 5\n}\nelse if (focus <=3000 && focus > 2000){\n focusstep = 3\n}\nelse{\n focusstep = 2\n}\n\n\nfocus = focus + sign * focusstep\n\nsign = msg.payload\nif (focus > 4000){\n distance = 6\n focus = 4000\n}\nelse if (focus > 1200 && focus <= 4000){\n distance = 737086 * Math.pow(focus, -1.4096)\n}\nelse if (focus <= 1200){\n distance = 999\n if (focus <=0){\n focus = 0\n }\n}\n\n\nglobal.set('focus', focus)\nmsg.focus = focus\nmsg.distance = distance\ndistance = distance * 10\nmsg.focuser = global.get('focuser')\nmsg.payload = String(distance.toFixed(1)) + 'mm'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 970, "y": 1300, "wires": [ [ "7219f62c9fdc6753", "282efe64332193c8", "704a9f89089d1f25" ] ] }, { "id": "f4a41b1e7b221486", "type": "change", "z": "1613373abaf77a2c", "name": "focuser f", "rules": [ { "t": "set", "p": "focuser", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 740, "y": 1260, "wires": [ [] ] }, { "id": "e9b3837b1ffb0360", "type": "change", "z": "1613373abaf77a2c", "name": "focuser t", "rules": [ { "t": "set", "p": "focuser", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 740, "y": 1340, "wires": [ [] ] }, { "id": "fd93843e238cc9ce", "type": "delay", "z": "1613373abaf77a2c", "name": "10ms", "pauseType": "delay", "timeout": "20", "timeoutUnits": "milliseconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 850, "y": 1300, "wires": [ [ "b70e8c24ee011258" ] ] }, { "id": "dfbfe28bac5c4221", "type": "ui_text", "z": "1613373abaf77a2c", "group": "90223f7ddc082321", "order": 2, "width": 1, "height": 1, "name": "MF", "label": "MF", "format": "", "layout": "col-center", "className": "", "x": 150, "y": 1300, "wires": [] }, { "id": "704a9f89089d1f25", "type": "function", "z": "1613373abaf77a2c", "name": "save", "func": "var file = 'cam_focus'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.focus)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1110, "y": 1360, "wires": [ [] ] }, { "id": "917a194be245384a", "type": "link in", "z": "1613373abaf77a2c", "name": "enable projectname", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 540, "wires": [ [ "f4b3112a9ec6c487" ] ] }, { "id": "65cef204b16f8741", "type": "link in", "z": "1613373abaf77a2c", "name": "enable shutter", "links": [ "2d76e5617f13cd6c", "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 580, "wires": [ [ "84d6b96c8ebaac96" ] ] }, { "id": "2aea1727dbea76ce", "type": "link in", "z": "1613373abaf77a2c", "name": "enable cropx", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 620, "wires": [ [ "9c6b48b7b4cc4e1a" ] ] }, { "id": "4f212b44aa487945", "type": "link in", "z": "1613373abaf77a2c", "name": "enable cropy", "links": [ "a0ba1aa77c5c8b7c", "a42c12e94f65fa01" ], "x": 55, "y": 660, "wires": [ [ "c470fd0b15356206" ] ] }, { "id": "6d1e12f51f9af0b6", "type": "link in", "z": "1613373abaf77a2c", "name": "start camchk", "links": [ "960912e90ba5b5bc" ], "x": 55, "y": 100, "wires": [ [ "12f1399b240830bf" ] ] }, { "id": "8ebd1dcb5db156ed", "type": "ui_text", "z": "1613373abaf77a2c", "group": "7aaf184330605300", "order": 2, "width": 6, "height": 1, "name": "", "label": "Current Status:", "format": " {{msg.payload}} ", "layout": "row-spread", "className": "", "x": 320, "y": 160, "wires": [] }, { "id": "94a7aec739f9266b", "type": "function", "z": "1613373abaf77a2c", "name": "loadS", "func": "var file = 'status_internal_cam'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\n\nif (data === 'no camera found'){\n msg.color = 'red'\n}\n\nreturn msg\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 160, "wires": [ [ "8ebd1dcb5db156ed" ] ] }, { "id": "2415272f42ce468c", "type": "link in", "z": "1613373abaf77a2c", "name": "start status", "links": [ "6c6ef2255a7d39e5" ], "x": 55, "y": 160, "wires": [ [ "94a7aec739f9266b" ] ] }, { "id": "a1e14624058e74cd", "type": "link in", "z": "1613373abaf77a2c", "name": "start routine settings", "links": [ "960912e90ba5b5bc" ], "x": 55, "y": 500, "wires": [ [ "f4b3112a9ec6c487", "107a030938cbfea9", "84d6b96c8ebaac96", "9c6b48b7b4cc4e1a", "c470fd0b15356206", "9e30e33a1520fee0" ] ] }, { "id": "1daf9e3a5bd5ab48", "type": "function", "z": "1613373abaf77a2c", "name": "msg", "func": "global.set('flag_pw', true)\nglobal.set('flag', true)\nmsg.enabled = true\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 750, "y": 880, "wires": [ [ "fb13752beddee9f2" ] ] }, { "id": "6d15f717d5a11002", "type": "function", "z": "1613373abaf77a2c", "name": "disable", "func": "msg.enabled = false\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 800, "wires": [ [ "e9b13dfd9f8d3711" ] ] }, { "id": "db7eea74d3bf892b", "type": "ui_toast", "z": "1613373abaf77a2c", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Finish", "cancel": "Continue", "raw": false, "className": "", "topic": "", "name": "", "x": 450, "y": 840, "wires": [ [ "0b8661103366f834" ] ] }, { "id": "0b8661103366f834", "type": "python3-function", "z": "1613373abaf77a2c", "name": "continue", "func": "from os import system\nfrom os.path import isfile\n\nif msg['payload'] == 'Continue':\n msg['enabled'] = True\n return msg,None\n\nbasepath = '/home/pi/OpenScan/'\ntemppath = basepath + 'tmp/tmp.jpg'\nzippath = basepath + 'tmp/tmp.zip'\nprojectcode = msg['projectcode']\n\nsystem('mv '+ zippath + ' ' + basepath + 'scans/' + projectcode + '.zip')\n\nif isfile(zippath):\n system('rm ' + zippath)\n\nreturn None, msg", "outputs": 2, "x": 600, "y": 880, "wires": [ [ "431f917c2541ae48", "579f2211199fd6ab" ], [ "1daf9e3a5bd5ab48", "579f2211199fd6ab" ] ] }, { "id": "482bc06e02eec5b9", "type": "link in", "z": "1613373abaf77a2c", "name": "preview", "links": [ "960912e90ba5b5bc", "fb13752beddee9f2" ], "x": 55, "y": 1200, "wires": [ [ "95439678bb2df2a2" ] ] }, { "id": "ea54fcc2.cfcc2", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "get dirs", "func": "from glob import glob\nimport os\nfrom zipfile import ZipFile\n\ndef set_stats(stat):\n try:\n with open(directory+set[:-4]+\"/\"+stat,\"r\") as file:\n stat=file.read()\n except:\n stat=\"\"\n return stat\n\ntable=[]\ndirectory=\"/home/pi/OpenScan/scans/\"\n\nfor d in glob(directory+\"*.zip\"):\n set=os.path.basename(d)\n\n try:\n with ZipFile(d, 'r') as f:\n photos = len(f.namelist())\n\n size = float(int(float(os.path.getsize(d))/100000))/10\n size_full= os.path.getsize(d)\n status=set_stats(\"status\")\n expiration=set_stats(\"expiration\")\n download=set_stats(\"download\")\n \n if len(download)!=0:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Download\":\"RESULT\",\n \"Size_full\":size_full,\n \n })\n else:\n table.append({\n \"Set\":set,\n \"Photos\":photos,\n \"Size\":str(size)+\"MB\",\n \"Date\":set[:16],\n \"Name\":(set[20:-4]),\n \"Status\":status,\n \"Size_full\":size_full,\n \n })\n except:\n pass\n\nmsg['payload']=table\nmsg['topic']=\"\"\nreturn msg", "outputs": 1, "x": 480, "y": 180, "wires": [ [ "b9a3a0f9.bcbea" ] ] }, { "id": "2f4c0f98.dee2", "type": "link in", "z": "4981d84ef1a366d1", "name": "filelist", "links": [ "960912e90ba5b5bc", "a4f09e25.02569", "ed35109311335099", "fb13752beddee9f2" ], "x": 355, "y": 140, "wires": [ [ "ea54fcc2.cfcc2" ] ] }, { "id": "b9a3a0f9.bcbea", "type": "ui_table", "z": "4981d84ef1a366d1", "group": "b5fdd57b.15eda8", "name": "", "order": 1, "width": 13, "height": 7, "columns": [ { "field": "Date", "title": "Date", "width": "150", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Name", "title": "Name", "width": "210", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Photos", "title": "Photos", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Size", "title": "Size", "width": "80", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } }, { "field": "Status", "title": "Status", "width": "140", "align": "center", "formatter": "plaintext", "formatterParams": { "target": "_blank" } } ], "outputs": 1, "cts": true, "x": 610, "y": 180, "wires": [ [ "50710948.71c308", "4082b136.dae18", "834046a4.647938", "0c387c0291d6c131" ] ] }, { "id": "952ce286.4ffd4", "type": "ui_text", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "order": 3, "width": 6, "height": 1, "name": "Status", "label": "Status", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 250, "y": 60, "wires": [] }, { "id": "d4383424.7807c8", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "upload", "func": "import os\nfrom OpenScan import OpenScanCloud, load_str, load_int, save\n\nbasedir = '/home/pi/OpenScan/'\n\nif load_str(\"feedback_terms\")==\"False\":\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic'] = 'OpenScanCloud - Terms of Use'\n return None,msg\n\nmsg = msg['payload']\n\ndef upload(filelist, ulinks):\n i = 0\n for file in filelist:\n link = ulinks[i]\n save('status_cloud', 'uploading ' + str(i+1) + '/' + str(len(filelist)))\n cmd = 'curl -# -X POST ' + link + ' --header Content-Type:application/octet-stream --data-binary @\"' + file + '\" 2>&1 | tee /home/pi/OpenScan/settings/status_uploadprogress'\n i = i+1\n os.system(cmd)\n\n########\nif not os.path.isfile(basedir + 'settings/token'):\n msg['flag'] = True\n save('status_cloud', 'please enter token first')\n return msg\nwith open(basedir + 'settings/token', 'r') as file:\n token = file.read().strip('\\n')\n\n########\nr = OpenScanCloud('getTokenInfo', {'token':token})\n\nif r.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n save('status_cloud', 'invalid/missing token')\n return None,msg\nelif r.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nmsg1 = r.json()\n\n########\nif msg['Photos'] > msg1['limit_photos'] or msg['Size_full'] > msg1['limit_filesize']:\n msg['flag'] = True\n save('status_cloud', 'limit(s) exceeded')\n return msg\n\n########\ntemp = OpenScanCloud('getProjectInfo', {'token':token, 'project':msg['Set']})\nif temp.status_code not in (200,401):\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nif temp.status_code != 401:\n temp = temp.json()\n if 'status' in temp:\n if temp['status'] != 'created':\n save('status_cloud','already exists')\n with open(basedir + 'scans/' + msg['Set'][:-4] + '/status', 'w') as file:\n file.write(temp['status'])\n return msg\n#####\n\nmsg2={}\nmsg2['token'] = token\nmsg2['parts'] = 1\nmsg['partslist']=[]\n\n#######\nsize_to_split = load_int('osc_splitsize')\n\nif msg['Size_full'] > size_to_split:\n tempdir = basedir + 'tmp/split/'\n if os.path.isdir(tempdir):\n os.system('rm -r ' + tempdir)\n os.mkdir(tempdir)\n save('status_cloud', 'zipping files, please wait ...')\n cmd = 'split -b ' + str(size_to_split) + ' ' + basedir + 'scans/' + msg['Set'] + ' ' + tempdir + msg['Set']\n os.system(cmd)\n save('status_cloud', 'zip done')\n list = os.listdir(tempdir)\n for l in list:\n msg['partslist'].append(tempdir + l)\n msg['partslist'].sort()\n msg2['parts']=len(msg['partslist'])\nelse:\n msg['partslist'] = [basedir + 'scans/' +msg['Set']]\n\n#######\nmsg2['photos'] = msg['Photos']\nmsg2['filesize'] = msg['Size_full']\nmsg2['project'] = msg['Set']\n\nr = OpenScanCloud('createProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Connection to OpenScanCloud failed'\n msg['payload'] = 'Please check your internet connection'\n return None,msg\n\nmsg1 = r.json()\n\nif not os.path.isdir(basedir+ 'scans/' + msg['Set'][:-4]):\n os.mkdir(basedir+ 'scans/' + msg['Set'][:-4])\nwith open(basedir+ 'scans/' + msg['Set'][:-4]+'/status', 'w+') as file:\n file.write('prepared')\n\nsave('status_cloud', 'uploading')\nupload(msg['partslist'], msg1['ulink'])\n\nr = OpenScanCloud('startProject', msg2)\nif r.status_code != 200:\n msg['topic'] = 'Upload failed'\n msg['payload'] = 'please try again'\n save('status_cloud', 'upload failed')\n return None,msg\n\nsave('status_cloud', 'uploaded')\n\nsave('status_cloud', 'project started')\n\ntry:\n os.system('rm -r ' + tempdir)\nexcept:\n pass\n\nreturn msg", "outputs": 2, "x": 530, "y": 420, "wires": [ [ "9a132ab1.b21658" ], [ "3d16b3789632784d", "9a132ab1.b21658" ] ] }, { "id": "50710948.71c308", "type": "change", "z": "4981d84ef1a366d1", "name": "set", "rules": [ { "t": "set", "p": "set", "pt": "global", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 750, "y": 180, "wires": [ [ "ada1b6f7cccc9344" ] ] }, { "id": "834046a4.647938", "type": "ui_text", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "order": 4, "width": 6, "height": 1, "name": "Set", "label": "Set:", "format": "{{msg.payload.Name}}", "layout": "row-spread", "className": "", "x": 750, "y": 220, "wires": [] }, { "id": "9a132ab1.b21658", "type": "change", "z": "4981d84ef1a366d1", "name": "flag.true", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "true", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 700, "y": 420, "wires": [ [ "8689e938.dd9e38" ] ] }, { "id": "3c67e97b.9d19a6", "type": "function", "z": "4981d84ef1a366d1", "name": "enable", "func": "if (global.get('flag') === false){\n msg.enabled = false\n msg.color=\"white\"\n}\nelse{\n msg.enabled = true\n msg.color=\"red\"\n \n}\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 130, "y": 340, "wires": [ [ "7a93d1e18254685c", "e434ef42bd6b92e8", "d5d840183025d91b", "ab9e90ab5a53a0dd", "478994f671a3907d" ] ] }, { "id": "bfc01f26.c32cf", "type": "change", "z": "4981d84ef1a366d1", "name": "flag.false", "rules": [ { "t": "set", "p": "flag", "pt": "global", "to": "false", "tot": "bool" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 420, "y": 460, "wires": [ [ "f20f2dbc.0f123" ] ] }, { "id": "b33d604c.5f1a6", "type": "link in", "z": "4981d84ef1a366d1", "name": "enable cloud", "links": [ "4082b136.dae18", "8689e938.dd9e38", "e9b13dfd9f8d3711", "f20f2dbc.0f123", "fb13752beddee9f2" ], "x": 35, "y": 340, "wires": [ [ "3c67e97b.9d19a6" ] ] }, { "id": "f6bd1a04.470838", "type": "change", "z": "4981d84ef1a366d1", "name": "set", "rules": [ { "t": "set", "p": "payload", "pt": "msg", "to": "set", "tot": "global" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 410, "y": 420, "wires": [ [ "d4383424.7807c8" ] ] }, { "id": "4082b136.dae18", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "links": [ "b33d604c.5f1a6", "87574a42938afec4" ], "x": 715, "y": 140, "wires": [] }, { "id": "f20f2dbc.0f123", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 515, "y": 460, "wires": [] }, { "id": "8689e938.dd9e38", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "8367cfa0bf5bc5df", "b33d604c.5f1a6", "149e2e46b9623a2d" ], "x": 795, "y": 420, "wires": [] }, { "id": "15de0ebb.616d61", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 380, "wires": [ [ "a7d89487.ee8858" ] ] }, { "id": "a7d89487.ee8858", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\ntry:\n os.remove(dir+msg['Set'])\n shutil.rmtree(dir+msg['Set'][:-4])\nexcept:\n pass\nreturn msg", "outputs": 1, "x": 690, "y": 380, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "a4f09e25.02569", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "links": [ "2f4c0f98.dee2", "c20357dd.374108", "e9aab326.a6896", "edd22cc7.befe1", "19b81967.49db87", "8ee1b3bb.7b0b3", "d5246b3cc796afc6" ], "x": 775, "y": 360, "wires": [] }, { "id": "7a93d1e18254685c", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "92c98e6ce7cd25f9" ], "x": 235, "y": 460, "wires": [] }, { "id": "4d99c601c9881680", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "refresh", "func": "from time import sleep\nimport os\nfrom OpenScan import load_str, OpenScanCloud, save, load_bool\n\nbasepath = '/home/pi/OpenScan/scans/'\n\nif load_bool(\"terms\")==False:\n msg['payload']=\"Please read and agree to the Terms of Use (See Settings Menu) before you can use the OpenScanCloud\"\n msg['topic']='OpenScanCloud - Terms of Use'\n return None,msg\n\nsave('status_cloud','refreshing')\ntoken = load_str('token')\n\ntest = OpenScanCloud('getTokenInfo',{'token':token})\nif test.status_code == 400:\n msg['topic'] = 'Invalid Token'\n msg['payload'] = 'Please enter a valid token (settings --> OpenScanCloud)'\n return None,msg\nelif test.status_code == 200:\n pass\nelse:\n msg['topic'] = 'Connection Error'\n msg['payload'] = 'Not able to establish a connection to OpenScanCloud.'\n return None,msg\n\nstats = test.json()\nfor i in stats:\n save('osc_'+i, stats[i])\n pass\n\nmsg={}\nprojects = []\nfor i in os.listdir(basepath):\n if os.path.isdir(basepath + i):\n if os.path.isfile(basepath + i + '/status'):\n with open(basepath + i + '/status', 'r') as file:\n status = file.read().strip('\\n')\n if status in ['expired', 'processing done', 'processing failed']:\n continue\n projects.append(i)\n\nfor p in projects:\n r = OpenScanCloud('getProjectInfo',{'token':token, 'project':p+'.zip'})\n if r.status_code == 200:\n answer = r.json()\n if answer == {}:\n os.system('sudo rm -r ' + basepath + p)\n else:\n with open(basepath + p + '/status', 'w+') as file:\n file.write(answer['status'])\n with open(basepath + p + '/download', 'w+') as file:\n file.write(answer['dlink'])\n\nmsg['list'] = projects\nsleep(0.5)\nsave('status_cloud','ready')\nreturn msg, None\n", "outputs": 2, "x": 320, "y": 180, "wires": [ [ "ea54fcc2.cfcc2", "b42e061fb1f1f3d7" ], [ "6434e713f088012b" ] ] }, { "id": "372e95797a3f2f3b", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "limit :)", "func": "from time import sleep\n\nmsg2={}\nmsg2['enabled'] = True\n\nmsg['enabled'] = False\nnode.send(msg)\n\nwait = 15\n\nfor i in range (wait):\n msg['text'] = ' ('+ str(wait - i)+')'\n node.send(msg)\n\nmsg['enabled'] = True\nmsg['text']=\"\"\n\n\nreturn msg", "outputs": 1, "x": 90, "y": 220, "wires": [ [ "573edbfdb7500ddc" ] ] }, { "id": "573edbfdb7500ddc", "type": "delay", "z": "4981d84ef1a366d1", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 230, "y": 220, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "dacb1f078b624e10", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 340, "wires": [ [ "c8d65cc7c2ff7c36" ] ] }, { "id": "92c98e6ce7cd25f9", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "7a93d1e18254685c", "bd75f33b8a57c522" ], "x": 35, "y": 180, "wires": [ [ "c46e10b9c201913e" ] ] }, { "id": "3d16b3789632784d", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 690, "y": 460, "wires": [ [] ] }, { "id": "6434e713f088012b", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "Terms", "x": 470, "y": 220, "wires": [ [] ] }, { "id": "c8d65cc7c2ff7c36", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "del", "func": "import os\nimport shutil\n\ndir=\"/home/pi/OpenScan/scans/\"\n\nif msg['payload']==\"No\":\n return\n\nfor i in os.listdir(dir):\n if os.path.isdir(dir + i):\n shutil.rmtree(dir + i)\n else:\n os.remove(dir + i)\n\nreturn msg", "outputs": 1, "x": 690, "y": 340, "wires": [ [ "a4f09e25.02569" ] ] }, { "id": "6d471a5210505276", "type": "function", "z": "4981d84ef1a366d1", "name": "read", "func": "var file = 'status_cloud'\nvar file2 = 'status_uploadprogress'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nstatus = String(data);\n\nif (status.substr(0,9) === 'uploading'){\n data = fs.readFileSync(filepath+file2, 'utf8');\n progress = data.substr(data.length - 6)\n if (progress.substr(progress.length -1) === '%'){\n status = status + ' (' + progress + ')'\n }\n}\nmsg.status = status\nreturn msg\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 130, "y": 60, "wires": [ [ "952ce286.4ffd4" ] ] }, { "id": "f4e9a4bd79b4221f", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.payload = 'Are you sure to delete ALL saved image sets? This can not be undone!'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 340, "wires": [ [ "dacb1f078b624e10" ] ] }, { "id": "2806bf08ea21216d", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.Set=global.get('set').Set\nmsg.payload = 'Are you sure to delete the set and ALL associated files: ' + msg.Set + '?'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 410, "y": 380, "wires": [ [ "15de0ebb.616d61" ] ] }, { "id": "61990987acd0f263", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "6c6ef2255a7d39e5" ], "x": 35, "y": 60, "wires": [ [ "6d471a5210505276" ] ] }, { "id": "e8e488a6dd5d0b33", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "Download", "order": 5, "width": 3, "height": 1, "format": "\n
Download\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 900, "y": 260, "wires": [ [] ] }, { "id": "0c387c0291d6c131", "type": "function", "z": "4981d84ef1a366d1", "name": "msg", "func": "msg.download = '/scans/' + String(msg.payload.Set)\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 750, "y": 260, "wires": [ [ "e8e488a6dd5d0b33" ] ] }, { "id": "e5f38b4a07a5e278", "type": "link in", "z": "4981d84ef1a366d1", "name": "", "links": [ "960912e90ba5b5bc" ], "x": 655, "y": 220, "wires": [ [ "834046a4.647938" ] ] }, { "id": "e434ef42bd6b92e8", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "upload2", "order": 6, "width": 3, "height": 1, "format": "upload", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 420, "wires": [ [ "f6bd1a04.470838", "bfc01f26.c32cf" ] ] }, { "id": "c46e10b9c201913e", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "refresh", "order": 1, "width": 3, "height": 1, "format": "refresh{{msg.text}}", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 160, "y": 180, "wires": [ [ "372e95797a3f2f3b", "4d99c601c9881680" ] ] }, { "id": "d5d840183025d91b", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "del set", "order": 8, "width": 2, "height": 1, "format": "delete set", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 380, "wires": [ [ "2806bf08ea21216d" ] ] }, { "id": "ab9e90ab5a53a0dd", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "del ", "order": 9, "width": 2, "height": 1, "format": "delete all", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 270, "y": 340, "wires": [ [ "f4e9a4bd79b4221f" ] ] }, { "id": "478994f671a3907d", "type": "ui_template", "z": "4981d84ef1a366d1", "group": "db43d646.2074c8", "name": "combine", "order": 7, "width": 2, "height": 1, "format": "combine", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 280, "y": 500, "wires": [ [ "51bfd0fb7b1d292e" ] ] }, { "id": "189c1eed09624a7b", "type": "function", "z": "4981d84ef1a366d1", "name": "combine", "func": "combine = global.get('combine')\ncombine_set = global.get('set').Set\n\nif (combine === true && global.get('combine_set') !== combine_set){\n msg.set1 = global.get('combine_set')\n msg.set2 = combine_set\n global.set('combine', false)\n msg.topic = 'Combine the following two sets:'\n msg.payload = msg.set1 + '
' + msg.set2 + '
FILES WILL BE MERGED INTO ON FILE!'\n return msg\n}\nglobal.set('combine_set' , combine_set)\n\n", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 280, "y": 540, "wires": [ [ "1493398979a63775" ] ] }, { "id": "51bfd0fb7b1d292e", "type": "function", "z": "4981d84ef1a366d1", "name": "combine", "func": "global.set('combine', true)\ncombine_set = global.get('set').Set\nmsg.topic = 'Merge two sets into one (can not be undone)!'\nmsg.payload = combine_set\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 420, "y": 500, "wires": [ [] ] }, { "id": "da325be8e74179be", "type": "python3-function", "z": "4981d84ef1a366d1", "name": "combine", "func": "from os.path import getsize\nfrom shutil import copy\nfrom os import rename, remove\nimport zipfile as z\nfrom OpenScan import save\n\nfrom time import sleep\n\nif msg['payload'] != 'OK':\n return\n\nbasepath = '/home/pi/OpenScan/scans/'\ntmp1 = basepath + msg['set1']\ntmp2 = basepath + msg['set2']\n\nif getsize(tmp1) > getsize(tmp2):\n set1 = tmp1\n set2 = tmp2\nelse:\n set1 = tmp2\n set2 = tmp1\n\n#set 1 is larger and to be merged into\n\nzips = [set1, set2]\n\nwith z.ZipFile(set1, 'a') as z1:\n z2 = z.ZipFile(set2, 'r')\n i = 0\n for n in z2.namelist():\n i += 1\n n2 = n\n save('status_cloud','combining ' + str(i) + '/' + str(len(z2.namelist())))\n while 'X'+n in z1.namelist():\n n = 'X' + n\n z1.writestr('X'+n, z2.open(n2).read())\nsave('status_cloud','ready')\n\nos.rename(set1, set1[:-4] + 'X.zip')\nos.remove(set2)\n\nreturn msg", "outputs": 1, "x": 560, "y": 540, "wires": [ [ "ed35109311335099" ] ] }, { "id": "ed35109311335099", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "809c9427e14e2448", "2f4c0f98.dee2" ], "x": 655, "y": 540, "wires": [] }, { "id": "1493398979a63775", "type": "ui_toast", "z": "4981d84ef1a366d1", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "Combine", "x": 420, "y": 540, "wires": [ [ "da325be8e74179be" ] ] }, { "id": "ada1b6f7cccc9344", "type": "link out", "z": "4981d84ef1a366d1", "name": "combine", "mode": "link", "links": [ "6dd356510c446cf4" ], "x": 835, "y": 180, "wires": [] }, { "id": "6dd356510c446cf4", "type": "link in", "z": "4981d84ef1a366d1", "name": "combine", "links": [ "ada1b6f7cccc9344" ], "x": 175, "y": 540, "wires": [ [ "189c1eed09624a7b" ] ] }, { "id": "b42e061fb1f1f3d7", "type": "link out", "z": "4981d84ef1a366d1", "name": "", "mode": "link", "links": [ "397ab7f44b893c89" ], "x": 435, "y": 140, "wires": [] }, { "id": "52858b4eceacc902", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "", "group": "3b4bd36726be16d5", "order": 4, "width": 2, "height": 1, "passthru": false, "label": "Terms", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 90, "y": 300, "wires": [ [ "f99ec8781a33ec7d" ] ] }, { "id": "7dc39bd847d16ded", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Agree", "cancel": "Disagree", "raw": true, "className": "", "topic": "", "name": "", "x": 410, "y": 300, "wires": [ [ "5f849178998d9082" ] ] }, { "id": "cc3cb10f2ea3f8b8", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "blink Light1", "func": "import RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nfrom OpenScan import ringlight\nfrom time import sleep\n\ndelay = 0.1\nringlight(2,False)\n\nfor i in range (5):\n ringlight(1,True)\n sleep(delay)\n ringlight(1,False)\n sleep(delay)", "outputs": 1, "x": 310, "y": 420, "wires": [ [] ] }, { "id": "d114f4d4d7f31981", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "reboot", "func": "import os\nfrom time import sleep\nsleep(1.5)\nimport RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nos.system('sudo reboot')\n", "outputs": 1, "x": 290, "y": 380, "wires": [ [] ] }, { "id": "f52d4d86b39aeb6b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "shutdown", "func": "import os\nfrom time import sleep\nsleep(1.5)\nimport RPi.GPIO as GPIO\nGPIO.setwarnings(False)\nGPIO.cleanup()\nos.system('sudo shutdown -h now')", "outputs": 1, "x": 300, "y": 460, "wires": [ [] ] }, { "id": "15a0a2f431ce55c3", "type": "comment", "z": "017bd4e4a428bee5", "name": "General Settings", "info": "", "x": 120, "y": 260, "wires": [] }, { "id": "87a403b9a09aa38d", "type": "comment", "z": "017bd4e4a428bee5", "name": "Network", "info": "", "x": 120, "y": 560, "wires": [] }, { "id": "ca4afadb5b21751f", "type": "comment", "z": "017bd4e4a428bee5", "name": "Info Texts", "info": "", "x": 100, "y": 120, "wires": [] }, { "id": "2a0f9919.4c9a86", "type": "comment", "z": "017bd4e4a428bee5", "name": "OpenScanCloud", "info": "", "x": 140, "y": 880, "wires": [] }, { "id": "27c6b221c90ed9e1", "type": "exec", "z": "017bd4e4a428bee5", "command": "sudo iwlist wlan0 scan | grep ESSID | sed 's/ESSID://g;s/\"//g;s/^ *//;s/ *$//'", "addpay": false, "append": "", "useSpawn": "false", "timer": "", "winHide": false, "oldrc": false, "name": "scan", "x": 270, "y": 720, "wires": [ [ "b05cf92302a5c112" ], [ "e9677b85856b5873" ], [] ] }, { "id": "b05cf92302a5c112", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "WIFI", "func": "msg['options']=[]\n\nfor i in msg['payload'].split('\\n'):\n if i not in msg['options'] and i!=\"\":\n msg['options'].append(i)\n \nif len(msg['options']) != 0:\n msg['enabled']=True\n\nreturn msg", "outputs": 1, "x": 390, "y": 700, "wires": [ [ "59c9f67283ba1709" ] ] }, { "id": "da5ddaf4cc25b8c8", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "search", "group": "3b4bd36726be16d5", "order": 11, "width": 3, "height": 1, "passthru": false, "label": "Search Wifi", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "true", "payloadType": "bool", "topic": "", "topicType": "str", "x": 110, "y": 660, "wires": [ [ "27c6b221c90ed9e1", "51521bc6eb44cde5" ] ] }, { "id": "59c9f67283ba1709", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "", "label": "", "tooltip": "", "place": "Select Wifi", "group": "3b4bd36726be16d5", "order": 14, "width": 6, "height": 1, "passthru": true, "multiple": false, "options": [], "payload": "", "topic": "", "topicType": "str", "className": "", "x": 540, "y": 660, "wires": [ [ "2bb52656f9554dab" ] ] }, { "id": "b2d7d6a730f7dca6", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Reset Wifi", "group": "3b4bd36726be16d5", "order": 12, "width": 3, "height": 1, "passthru": false, "label": "Reset Wifi", "tooltip": "", "color": "red", "bgcolor": "", "className": "", "icon": "", "payload": "Delete all prior wifi connections? (You will need to reconnect to the OpenScan device by Ethernet or manually modify the wpa_supplicant.conf)", "payloadType": "str", "topic": "", "topicType": "str", "x": 130, "y": 820, "wires": [ [ "78985ac6d3bcdf60" ] ] }, { "id": "c3b8faac9ebb2c80", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Reset Wifi", "func": "from time import sleep\n\nif msg['payload']!=\"Yes\":\n return\n\ntemp_dir = '/home/pi/OpenScan/tmp/wpa_empty.log'\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\nwith open(temp_dir, 'w+') as file:\n file.write('update_config=1\\nctrl_interface=DIR=/var/run/wpa_supplicant\\ncountry=de\\n\\n')\nos.system('sudo mv '+ temp_dir + ' ' + wpa_dir)\nos.system('sudo wpa_cli -i wlan0 reconfigure')\nsleep(3)\nos.system('sudo systemctl restart nodered')\nreturn msg", "outputs": 1, "x": 460, "y": 820, "wires": [ [] ] }, { "id": "78985ac6d3bcdf60", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "No", "cancel": "Yes", "raw": false, "className": "", "topic": "", "name": "", "x": 290, "y": 820, "wires": [ [ "c3b8faac9ebb2c80" ] ] }, { "id": "4f7f49b12c2d2572", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "add Wifi", "func": "from time import sleep\nsleep(0.1)\n\nos.system('sudo wpa_cli -i wlan0 reconfigure')\n\nreturn msg", "outputs": 1, "x": 1340, "y": 680, "wires": [ [] ] }, { "id": "ebcc98685059b9d4", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "password", "x": 800, "y": 660, "wires": [ [ "68204a14528ab842" ] ] }, { "id": "68204a14528ab842", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "if msg['payload'] == 'Cancel':\n return\n\nmsg['password'] = msg['payload']\nmsg['payload']='Enter country code (ISO 3166-1 alpha-2, see: Wikipedia)'\n\n\nreturn msg", "outputs": 1, "x": 930, "y": 660, "wires": [ [ "852edf901bdec9c5" ] ] }, { "id": "852edf901bdec9c5", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "Save", "cancel": "Cancel", "raw": true, "className": "", "topic": "", "name": "country", "x": 1060, "y": 660, "wires": [ [ "1b09d634e3d9357b" ] ] }, { "id": "1b09d634e3d9357b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "modWPA", "func": "if msg['payload'] == 'Cancel':\n return\n\nif len(msg['payload'])!=2:\n msg['payload'] = 'invalid country code'\n return msg,None\n\nwpa_dir = '/etc/wpa_supplicant/wpa_supplicant.conf'\n\ntemp_dir = '/home/pi/OpenScan/tmp/wpa'\n\ncode = msg['payload'].upper()\nssid = msg['ssid']\npassword = msg['password']\n\nif len(code) != 2:\n msg['topic'] = 'ERROR'\n msg['payload'] = 'invalid country code (see ISO 3166-1 alpha-2)'\n return msg\n\nwith open(wpa_dir, 'r') as file:\n for i in file.readlines():\n if 'country=' in i:\n code_old=i.split('country=')[1][0:2]\n break\n\nwith open(wpa_dir, 'r') as file:\n wpa = file.read()\n if ssid in wpa:\n msg['topic'] = 'ERROR'\n msg['payload'] = 'Network already exists! If you have trouble connecting, please consider resetting the saved Wifi connections.'\n return msg\n wpa=wpa.replace('country=' + code_old, 'country=' + code)\n wpa=wpa + '\\nnetwork={\\n priority=10\\n ssid=\"'+ssid+'\"\\n psk=\"'+password+'\"\\n}\\n'\n\nwith open(temp_dir,'w+') as file:\n file.write(wpa)\nos.system('sudo mv '+temp_dir + ' ' + wpa_dir)\n\nmsg['topic'] = 'Updating Wifi'\nmsg['payload'] = 'reconnecting might take a moment'\nreturn msg,msg\n", "outputs": 2, "x": 1200, "y": 660, "wires": [ [ "03732a7d3b0c95aa" ], [ "4f7f49b12c2d2572" ] ] }, { "id": "03732a7d3b0c95aa", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 1350, "y": 640, "wires": [ [] ] }, { "id": "e97d17c6590138e2", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Cloud", "group": "3b4bd36726be16d5", "order": 2, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

OpenScanCloud

OpenScanCloud is a free/donation-based cloud processing service, which will convert your photos into 3d models using latest photogrammetry technology. Feel free to support the project with a small donation at BuyMeACoffee.

The only requirement to use this service is a one-time, free-of-charge registration (which is solely an anti-spam measure). By filling out the registration form, you will receive an individual access token.

Register

In order to use the OpenScanCloud, you will have to enter your name and email. It might take 1-3 days to create the access token, which will be sent to your mail address. Please check your spam folder.

Enter Token

Please enter your individual token here in order to activate the cloud functionality. The token will be verified immediately. In case of any problems, please contact cloud@openscan.eu

Terms

Please read the terms of use to understand what will happen to your data, when using the OpenScanCloud service.

Token

A shorted version of your token will be displayed here. Please include a copy of this shorted token in any support requests cloud@openscan.eu

Credit (GB)

Each token comes with a given amount of 'credit' which is another measure against spam. The given number in Gigabyte indicates the amount of data, that you can process on the servers. 

IMPORTANT: The credit can be increased at any time by sending a (nice) mail to cloud@openscan.eu

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 250, "y": 120, "wires": [ [ "f304680180a23479" ] ] }, { "id": "1969c709ef2fd1d5", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 7, "width": 0, "height": 0, "name": "", "label": "Credit (GB):", "format": "{{msg.credit}}", "layout": "row-spread", "className": "", "x": 730, "y": 1140, "wires": [] }, { "id": "397ab7f44b893c89", "type": "link in", "z": "017bd4e4a428bee5", "name": "OSCparameters", "links": [ "960912e90ba5b5bc", "9c51aa678f16980f", "b42e061fb1f1f3d7" ], "x": 465, "y": 1140, "wires": [ [ "a7fd00943edc380b" ] ] }, { "id": "bf6d941ad307ce22", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 270, "y": 960, "wires": [ [ "f22dfef37d5de773" ] ] }, { "id": "f22dfef37d5de773", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "import re\n\nif msg['payload'] == 'Cancel':\n return\n\nmail = msg['payload']\nemail_regex = re.compile(r\"[^@]+@[^@]+\\.[^@]+\")\n\nif email_regex.match(mail) != None:\n msg['mail'] = mail\n msg['topic'] = 'OpenScanCloud Registration (2/3)'\n msg['payload'] = 'Enter your first name'\n return msg\nmsg['payload'] = 'invalid input'\nreturn None,msg\n", "outputs": 2, "x": 410, "y": 960, "wires": [ [ "54602ee49ca022e7" ], [ "1505f3e72f971081" ] ] }, { "id": "1505f3e72f971081", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 1000, "wires": [ [] ] }, { "id": "54602ee49ca022e7", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 550, "y": 960, "wires": [ [ "f9efcb87b74abbd4" ] ] }, { "id": "510dbe4d76253bd6", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "SUBMIT", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 830, "y": 960, "wires": [ [ "600b2306caed1640" ] ] }, { "id": "600b2306caed1640", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "import requests\nimport os\nfrom OpenScan import OpenScanCloud\n\nif msg['payload'] == 'Cancel':\n return\n\nmsg['lastname'] = msg['payload']\n\nmsg2 = {}\n\nfor i in ['forename','lastname','mail']:\n msg2[i] = msg[i]\n\nr = OpenScanCloud('requestToken',msg2)\n\nstatus = r.status_code\n\nmsg['topic'] = 'OpenScanCloud Registration - Success'\nmsg['payload'] = 'registration done, you will get an email with your token within the next one or two days :)'\n\nif status != 200:\n msg['topic'] = 'OpenScanCloud Registration - Failed'\n msg['payload'] = 'Registration failed, please try again.'\n\nmsg['status'] = status\n\nreturn msg", "outputs": 1, "x": 970, "y": 960, "wires": [ [ "bbad1ab5f8f63fb7" ] ] }, { "id": "d34cd203725bac15", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Register", "group": "3b4bd36726be16d5", "order": 5, "width": 2, "height": 1, "passthru": false, "label": "Register", "tooltip": "", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "Please enter your email address:", "payloadType": "str", "topic": "Requesting an OpenScanCloud Token", "topicType": "str", "x": 120, "y": 960, "wires": [ [ "bf6d941ad307ce22" ] ] }, { "id": "bbad1ab5f8f63fb7", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 1110, "y": 960, "wires": [ [] ] }, { "id": "a7fd00943edc380b", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "msg", "func": "from OpenScan import load_int\n\nmsg = {}\n\ntry:\n msg['credit'] = float(int(load_int('osc_credit')/10000000))/100\n return msg\nexcept:\n pass", "outputs": 1, "x": 590, "y": 1140, "wires": [ [ "1969c709ef2fd1d5" ] ] }, { "id": "f99ec8781a33ec7d", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.payload = 'This is a free piece of software and it is provided as is, without any warranty.
There might be functions that need a connection to the internet: '+\n '

By entering a token and/or pressing UPLOAD, the device will create a connection to my servers, where the associated user information is stored (token, email, name, credit, limit_photos, limit_filesize)'+\n 'The selected image set will be uploaded to Dropbox Inc via one-time temporary upload link. The files will be saved on Dropbox Inc. for a maximum of 7 days. (+the time Dropbox Inc. will need to delete the files permanently)'+\n 'Processing will be done on my local servers, where the images get downloaded from Dropbox and processed on my workstations. The resulting 3D model will be uploaded to Dropbox and a link will be created and send to your email address from my google mail account.'+\n '

By uploading data to my servers, you agree, that I can use those images and derived 3d models for further research and to improve my services.'+\n 'The raw images and resulting 3d models will never be published without your explicit consent.'+ \n '

If you have any questions you can contact me at info@openscan.eu.'+ \n '

THE SOFTWARE IS PROVIDED AS IS WITHOUT '+\n 'WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE'+ \n 'AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,'+ \n 'WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE';\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 300, "wires": [ [ "7dc39bd847d16ded" ] ] }, { "id": "5f849178998d9082", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "if(msg.payload === 'Agree'){\n data = true;\n}\nelse{\n data = false;\n}\nvar file = 'terms'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nfs.writeFile(filepath+file, String(data), err => {\n if (err) {\n return msg\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 550, "y": 300, "wires": [ [] ] }, { "id": "725fd0cab0bddc0e", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'hostname'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst data = fs.readFileSync(filepath+file, 'utf8');\nmsg.payload = String(data);\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 620, "wires": [ [ "49259adad52fc214" ] ] }, { "id": "49259adad52fc214", "type": "ui_text_input", "z": "017bd4e4a428bee5", "name": "", "label": "Hostname", "tooltip": "", "group": "3b4bd36726be16d5", "order": 13, "width": 6, "height": 1, "passthru": false, "mode": "text", "delay": "0", "topic": "Change hostname to:", "sendOnBlur": true, "className": "", "topicType": "str", "x": 550, "y": 620, "wires": [ [ "8001f7c361de7d8c" ] ] }, { "id": "51521bc6eb44cde5", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "msg.enabled = false\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 270, "y": 660, "wires": [ [ "59c9f67283ba1709" ] ] }, { "id": "2bb52656f9554dab", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "ssid = msg.payload\nmsg.topic = 'Add wifi network (' + ssid + ')'\nmsg.payload = 'Enter Wifi password:'\nmsg.ssid = ssid\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 670, "y": 660, "wires": [ [ "ebcc98685059b9d4" ] ] }, { "id": "ebce67b739d1891f", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "chk/change hostname", "func": "from OpenScan import save\n\nif msg['payload'] != 'OK':\n pass\n\nwith open('/etc/hostname', 'r') as file:\n old_hostname = file.read().replace('\\n','')\n\nhostname = msg['hostname']\nif len(hostname) < 4 :\n msg['payload'] = ' '\n msg['topic'] = 'ERROR - Hostname NOT changed'\n return msg\n \n\nwith open('/etc/hostname', 'w+') as file:\n file.write(hostname)\nos.system('echo ' + hostname + ' | sudo tee /etc/hostname')\nwith open('/etc/hosts', 'r') as file:\n temp = file.read()\ntemp = temp.replace(old_hostname,hostname)\nwith open('/etc/hosts', 'w') as file:\n file.write(temp)\nos.system('sudo hostnamectl set-hostname ' + hostname)\nos.system('sudo systemctl restart avahi-daemon')\nsave('hostname',hostname)\nmsg['payload'] = hostname\nmsg['topic'] = 'Success - Hostname changed'\nreturn msg\n", "outputs": 1, "x": 1160, "y": 620, "wires": [ [ "03732a7d3b0c95aa" ] ] }, { "id": "667ac2aba819f506", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "Confirm", "x": 940, "y": 620, "wires": [ [ "ebce67b739d1891f" ] ] }, { "id": "8001f7c361de7d8c", "type": "change", "z": "017bd4e4a428bee5", "name": "", "rules": [ { "t": "set", "p": "hostname", "pt": "msg", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 730, "y": 620, "wires": [ [ "667ac2aba819f506" ] ] }, { "id": "9bb0adbd716ce347", "type": "link in", "z": "017bd4e4a428bee5", "name": "reboot", "links": [ "16c76929f88df841", "fe3a855fee9e28c6", "d663dd83d71b8693" ], "x": 175, "y": 380, "wires": [ [ "d114f4d4d7f31981", "cc3cb10f2ea3f8b8" ] ] }, { "id": "fc9abb94c35eec56", "type": "link in", "z": "017bd4e4a428bee5", "name": "shutdown", "links": [ "597bfb653e8cddbf" ], "x": 175, "y": 460, "wires": [ [ "cc3cb10f2ea3f8b8", "f52d4d86b39aeb6b" ] ] }, { "id": "f9efcb87b74abbd4", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "if (msg.payload === 'Cancel'){\n return\n}\nmsg.forename = msg.payload\nmsg.topic = 'OpenScanCloud Registration (3/3)'\nmsg.payload = 'Enter your last name'\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 690, "y": 960, "wires": [ [ "510dbe4d76253bd6" ] ] }, { "id": "adc206aa8edd1e41", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "OSC", "group": "db43d646.2074c8", "order": 2, "width": 3, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Files&Cloud

Refresh

You can refresh the status of the processing of your files in the OpenScanCloud. Make sure to read and agree the terms of use (in settings menu) before using the OpenScanCloud. Do not spam this button, as this might lead to temporary/permanent suspension of your IP address.

The status (in the table) of the individual sets in the file list will be updated to one of the following:

Created - you started the upload of your image set. If you are stuck on this status, please try to restart the upload.

Initialized - all files have been uploaded and processing will start as soon as possible

File approved - the server received and verified your files

Processing started - your files are currently being processed

Processing failed - there are various reasons why processing might fail. Please check the email for more details or contact me at cloud@openscan.eu

processing done - check your email, where you should find a link to the 3d model :)

Status (on the right column)

Indicates, what the device is currently up to.

Refreshing - updating all image set's status

Uploading - while transferring the image set to the OpenScanCloud servers. If the upload freezes, be patient. If nothing happens, reboot the device and restart the upload.

Project started - when the upload of a set was successful

Zipping - files larger then 200mb have to be split and re-zipped before uploading to the OpenScanCloud, the process might take a while depending on the filesize.

Combining - two sets into one might take up to a minute. 

Set

select a set from the file list by clicking on a row in the table

Download

Download the selected set from the OpenScan device to your computer/mobile/tablet

Upload

Upload the selected file to the OpenScanCloud

Combine

In order to combine two sets, select one set. Click the combine button and select the second set. A pop-up will appear, and you can confirm the operation. All images from the two sets will be merged into one set. The original image sets will be deleted!

Delete Set/All

Please keep in mind, that the memory of the SD card is relatively small, and thus you will have to delete individual or all photo sets from time to time.

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 470, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "e9677b85856b5873", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "Reset rfkill", "func": "from os import system\nif \"Interface doesn't support scanning\" in msg['payload']:\n system('rfkill unblock all')\n system('sudo ifconfig wlan0 up')\n return msg", "outputs": 1, "x": 410, "y": 740, "wires": [ [] ] }, { "id": "9b2bc9849aee310b", "type": "link in", "z": "017bd4e4a428bee5", "name": "changeHostname", "links": [ "ec2db55a99bbe3ee", "d5175561293ef490", "960912e90ba5b5bc" ], "x": 855, "y": 580, "wires": [ [ "8b9e3781511e9231" ] ] }, { "id": "8b9e3781511e9231", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "chk", "func": "with open('/etc/hostname', 'r') as file:\n old_hostname = file.read().replace('\\n','')\nif old_hostname == 'raspberrypi':\n msg['hostname'] = 'openscan'\n msg['payload'] = 'OK'\n return msg", "outputs": 1, "x": 950, "y": 580, "wires": [ [ "ebce67b739d1891f" ] ] }, { "id": "65b38bfeb3fee710", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 175, "y": 420, "wires": [ [ "cc3cb10f2ea3f8b8" ] ] }, { "id": "d3fc91d87d5d5f62", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 155, "y": 620, "wires": [ [ "725fd0cab0bddc0e" ] ] }, { "id": "cc9c4092edeb43cc", "type": "link in", "z": "017bd4e4a428bee5", "name": "enable projectname", "links": [ "960912e90ba5b5bc" ], "x": 155, "y": 700, "wires": [ [ "27c6b221c90ed9e1" ] ] }, { "id": "80bccc884b0be297", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "38783aea9cc317a6" ], "x": 1435, "y": 300, "wires": [] }, { "id": "25426d3582cc1236", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Enter Tk", "group": "3b4bd36726be16d5", "order": 3, "width": 2, "height": 1, "passthru": false, "label": "Enter Token", "tooltip": "testtesttest", "color": "", "bgcolor": "", "className": "", "icon": "", "payload": "Please enter your OpenScanCloud Token:", "payloadType": "str", "topic": "Token", "topicType": "str", "x": 120, "y": 1060, "wires": [ [ "c690fed61878ce83" ] ] }, { "id": "c690fed61878ce83", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "prompt", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "Cancel", "raw": false, "className": "", "topic": "", "name": "", "x": 270, "y": 1060, "wires": [ [ "781f672b78ea70b2" ] ] }, { "id": "781f672b78ea70b2", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "save", "func": "import requests\nimport os\nfrom OpenScan import save, OpenScanCloud\n\nif msg['payload'] == 'Cancel':\n return\n\ntoken = msg['payload']\n\nif len(msg['payload']) >=14:\n try:\n r = OpenScanCloud('getTokenInfo', {'token':token})\n if r.status_code != 200:\n msg['topic'] = 'Error'\n msg['payload'] = 'Invalid Token'\n return msg \n \n msg1 = r.json()\n save('osc_credit',msg1['credit'])\n save('osc_limit_filesize',msg1['limit_filesize'])\n save('osc_limit_photos',msg1['limit_photos'])\n save('token',token)\n msg['topic'] = 'Success'\n msg['payload'] = 'Token verified and saved'\n except:\n msg['topic'] = 'Error'\n msg['payload'] = 'Could not verify token, please check your internet connection.'\n return msg \n\n\nelse:\n msg['topic'] = 'Error'\n msg['payload'] = 'Invalid tokenformat'\n\nreturn msg", "outputs": 1, "x": 430, "y": 1060, "wires": [ [ "5e4b3bdb0a26052d", "4faf2fbd3cf6aa3a", "a7fd00943edc380b" ] ] }, { "id": "6d2c65d7e1d928ce", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 6, "width": 0, "height": 0, "name": "", "label": "Token", "format": "{{msg.payload}}", "layout": "row-spread", "className": "", "x": 710, "y": 1100, "wires": [] }, { "id": "5e4b3bdb0a26052d", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "text", "func": "from OpenScan import load_str\n\ntoken = load_str('token')[0:8]\nmsg['payload']= token + '...'\nif len(token)==0:\n msg['payload']=\"enter token\"\nreturn msg", "outputs": 1, "x": 590, "y": 1100, "wires": [ [ "6d2c65d7e1d928ce" ] ] }, { "id": "e0965e490d53617f", "type": "link in", "z": "017bd4e4a428bee5", "name": "token", "links": [ "960912e90ba5b5bc" ], "x": 465, "y": 1100, "wires": [ [ "5e4b3bdb0a26052d" ] ] }, { "id": "4faf2fbd3cf6aa3a", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": false, "className": "", "topic": "", "name": "", "x": 610, "y": 1060, "wires": [ [] ] }, { "id": "36b3b36c399ac7db", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "get update", "func": "import json\nimport requests\nfrom OpenScan import load_str\n\nif not msg['payload']:\n msg['status'] = '--READY--'\n return msg\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'no internet connection'\n return msg\n\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg['status'] = 'checking updates'\n\nreturn msg, msg", "outputs": 2, "x": 310, "y": 1380, "wires": [ [ "1d9f24f41817a2de" ], [ "0c1d054fa7f2afe8" ] ] }, { "id": "48cd023b07c39a94", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 20, "width": 0, "height": 0, "name": "", "label": "Status:", "format": "{{msg.status}}", "layout": "row-spread", "className": "", "x": 170, "y": 1300, "wires": [] }, { "id": "0c1d054fa7f2afe8", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "check files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str\nfrom time import sleep\n\nsleep(1)\n\n\nscope = load_str('update_type')\nmsg['scope'] = scope\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_old = updatepath + 'update.json'\n\ncounter = 0\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n if os.path.isfile(temp):\n filesize = os.path.getsize(temp)\n msg[scope][i]['filesize2'] = filesize\n if filesize == msg[scope][i]['filesize']:\n msg[scope][i]['update'] = False\n continue\n msg[scope][i]['update'] = True\n\n counter += 1\n\nif counter == 0:\n msg['status'] = 'No new update available'\nelse:\n msg['status'] = 'New update available'\n msg['topic'] = msg['status']\n msg['payload'] = 'Install & reboot now?'\n\nmsg['counter'] = counter # new file counter\n\nreturn msg\n", "outputs": 1, "x": 470, "y": 1400, "wires": [ [ "1d9f24f41817a2de", "7097687ddcc4fa8e" ] ] }, { "id": "612a7556ab11cf7d", "type": "link in", "z": "017bd4e4a428bee5", "name": "", "links": [ "960912e90ba5b5bc" ], "x": 75, "y": 1340, "wires": [ [ "e447af84ecc540ad", "72ca6c281c43acd7", "dadf823225aa34c4", "9df2481a03f24d0a" ] ] }, { "id": "7097687ddcc4fa8e", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "NO", "cancel": "YES", "raw": false, "className": "", "topic": "", "name": "", "x": 630, "y": 1420, "wires": [ [ "f9fe26a38501bcad", "77859c0059f8a49e" ] ] }, { "id": "9112e8b2865ea436", "type": "link in", "z": "017bd4e4a428bee5", "name": "update status", "links": [ "1d9f24f41817a2de", "26dae88a383eee97" ], "x": 75, "y": 1300, "wires": [ [ "48cd023b07c39a94" ] ] }, { "id": "1d9f24f41817a2de", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "9112e8b2865ea436" ], "x": 575, "y": 1380, "wires": [] }, { "id": "f9fe26a38501bcad", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "download files", "func": "import json\nimport requests\nimport shutil\nfrom OpenScan import load_str\n\n\nscope = msg['scope']\n\nupdatepath = '/home/pi/OpenScan/updates/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\n\nif msg['payload'] != 'YES':\n return\n\ncounter = 0\n\nfor i in msg[scope]:\n if msg[scope][i]['update'] == False:\n continue\n \n filepath = msg[scope][i]['dst']\n temp = updatepath + os.path.basename(filepath)\n \n r = requests.get(url + msg[scope][i]['src'])\n if r.status_code != 200:\n msg['status'] = 'downloading ' + msg[scope][i]['src'] + ' failed'\n return msg\n with open(temp, 'wb+') as file:\n file.write(r.content)\n shutil.copy(temp, msg[scope][i]['dst'])\n counter += 1\n\nmsg['status'] = 'Installed ' + str(counter) + ' of ' + str(msg['counter']) + ' - restarting ...'\n\nif counter == msg['counter']:\n updatepath_temp = updatepath + 'update_temp.json'\n updatepath_old = updatepath + 'update.json'\n shutil.move(updatepath_temp, updatepath_old)\n\nreturn msg\n", "outputs": 1, "x": 800, "y": 1440, "wires": [ [ "26dae88a383eee97", "d663dd83d71b8693" ] ] }, { "id": "77859c0059f8a49e", "type": "function", "z": "017bd4e4a428bee5", "name": "msg", "func": "if (msg.payload == 'YES'){\n msg.status = 'Installing updates'\n return msg}", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 770, "y": 1400, "wires": [ [ "26dae88a383eee97" ] ] }, { "id": "26dae88a383eee97", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "9112e8b2865ea436" ], "x": 925, "y": 1400, "wires": [] }, { "id": "e447af84ecc540ad", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\n\nmsg.payload = data\n\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 1380, "wires": [ [ "36b3b36c399ac7db" ] ] }, { "id": "d663dd83d71b8693", "type": "link out", "z": "017bd4e4a428bee5", "name": "", "mode": "link", "links": [ "9bb0adbd716ce347" ], "x": 925, "y": 1440, "wires": [] }, { "id": "444acd32e7578254", "type": "python3-function", "z": "017bd4e4a428bee5", "name": "create beta new", "func": "import json\nimport requests\nimport shutil\n\n#scope = 'main'\nscope = 'mini'\n\nupdatepath = '/home/pi/OpenScan/tmp/'\nurl = 'https://raw.githubusercontent.com/OpenScanEu/OpenScan2/main/update/'\nupdatepath_temp = updatepath + 'update_temp.json'\nupdatepath_old = updatepath + 'update.json'\n\n\n## load update.json\nr = requests.get(url + 'update.json')\n\nif r.status_code != 200:\n msg['status'] = 'download update.json failed'\n return msg\n\nmsg = {}\nwith open(updatepath_temp, 'wb+') as file:\n file.write(r.content)\nwith open(updatepath_temp, 'r') as file:\n msg = json.load(file)\n\nmsg2 = msg.copy()\ntry:\n del msg[scope]\nexcept:\n pass\n\nmsg[scope]={}\nmsg[scope]['1'] = {}\nmsg[scope]['1']['src'] = scope + '/fla.py'\nmsg[scope]['1']['dst'] = '/home/pi/OpenScan/files/fla.py'\n\nmsg[scope]['2'] = {}\nmsg[scope]['2']['src'] = scope + '/Arducam.py'\nmsg[scope]['2']['dst'] = '/usr/lib/python3/dist-packages/Arducam.py'\n\nmsg[scope]['3'] = {}\nmsg[scope]['3']['src'] = scope + '/OpenScan.py'\nmsg[scope]['3']['dst'] = '/usr/lib/python3/dist-packages/OpenScan.py'\n\nmsg[scope]['4'] = {}\nmsg[scope]['4']['src'] = scope + '/config.txt'\nmsg[scope]['4']['dst'] = '/boot/config.txt'\n\nmsg[scope]['5'] = {}\nmsg[scope]['5']['src'] = scope + '/flows.json'\nmsg[scope]['5']['dst'] = '/home/pi/OpenScan/settings/.node-red/flows.json'\n\nmsg[scope]['6'] = {}\nmsg[scope]['6']['src'] = scope + '/settings.js'\nmsg[scope]['6']['dst'] = '/root/.node-red/settings.js'\n\nmsg[scope]['7'] = {}\nmsg[scope]['7']['src'] = 'files/logo.jpg'\nmsg[scope]['7']['dst'] = '/home/pi/OpenScan/files/logo.jpg'\n\nfor i in msg[scope]:\n filepath = msg[scope][i]['dst']\n filesize = os.path.getsize(filepath)\n msg[scope][i]['filesize'] = filesize\n\nif os.path.isdir('/home/pi/OpenScan/tmp/update/'):\n os.system('rm -r /home/pi/OpenScan/tmp/update') \nos.makedirs('/home/pi/OpenScan/tmp/update/')\n\nwith open('/home/pi/OpenScan/tmp/update/update.json', 'w+') as f:\n json.dump(msg, f, indent=4)\n\nfor i in msg[scope]:\n if not os.path.isdir(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])):\n os.makedirs(os.path.dirname('/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src']))\n shutil.copy(msg[scope][i]['dst'], '/home/pi/OpenScan/tmp/update/' + msg[scope][i]['src'])\n\nmsg['payload'] = 'created with scope: ' + scope\n\nreturn msg", "outputs": 1, "x": 280, "y": 1780, "wires": [ [ "7f097823a90facb6" ] ] }, { "id": "7f097823a90facb6", "type": "debug", "z": "017bd4e4a428bee5", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 430, "y": 1780, "wires": [] }, { "id": "e547e40ff805742b", "type": "inject", "z": "017bd4e4a428bee5", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 120, "y": 1780, "wires": [ [ "444acd32e7578254" ] ] }, { "id": "5fe2d831c3ab1cf4", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 570, "y": 1340, "wires": [ [] ] }, { "id": "154716c51aae2b87", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Auto-check update availability", "tooltip": "", "group": "3b4bd36726be16d5", "order": 21, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 370, "y": 1340, "wires": [ [ "5fe2d831c3ab1cf4" ] ] }, { "id": "72ca6c281c43acd7", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'update_auto'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 1340, "wires": [ [ "154716c51aae2b87" ] ] }, { "id": "a0e996cbd2b18363", "type": "comment", "z": "017bd4e4a428bee5", "name": "Updates", "info": "", "x": 120, "y": 1240, "wires": [] }, { "id": "0dcf979b126c3e33", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 1, "width": 4, "height": 1, "name": "", "label": "", "format": "OPENSCANCLOUD", "layout": "col-center", "className": "", "x": 290, "y": 900, "wires": [] }, { "id": "50ab351d92165de8", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 9, "width": 4, "height": 1, "name": "", "label": "", "format": "NETWORK SETTINGS", "layout": "col-center", "className": "", "x": 270, "y": 560, "wires": [] }, { "id": "726819d40397f3ce", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Wifi", "group": "3b4bd36726be16d5", "order": 10, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Network Settings

Hostname

The device can be accessed through any browser in the same network. Therefore, you can either enter the device's IP address or the given hostname. The standard name is 'openscan' but it is highly recommended to change the name, when using multiple devices (e.g. 'openscan1', 'openscan2' ...)

Select Wifi

After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: ISO 3166 Country Code on Wikipedia

Search Wifi

You can manually refresh the list of available networks by pressing this button.

Reset Wifi

Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see glennklockwood.com for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 250, "y": 160, "wires": [ [ "f304680180a23479" ] ] }, { "id": "d07e9c092f0855eb", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 16, "width": 4, "height": 1, "name": "", "label": "", "format": "UPDATES", "layout": "col-center", "className": "", "x": 270, "y": 1240, "wires": [] }, { "id": "a85de9dee94dc786", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Updates&LOG", "group": "3b4bd36726be16d5", "order": 17, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Update&Log

Status

See whether new updates are available. It is highly recommended to use the latest firmware version. See OpenScan2 on Github.com for details and the source code.

Auto-Check update availability

Perform an automated update-check after each start of the device. If the device is connected to the internet, it will get the latest files from OpenScan2 on Github.com

This option is activated by default.

Check Updates

Alternatively, you can check for updates manually at any time by pressing this button.

Download Error Log

In case you encounter any errors with your device, please download the error log text and send a copy to info@openscan.eu or create an issue on Github.com

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 280, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "2968c5996fb6d98c", "type": "ui_template", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "name": "Download LOG", "order": 25, "width": 6, "height": 1, "format": "\n
Download error log\n
\n", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 140, "y": 1520, "wires": [ [] ] }, { "id": "d66c08c5f0134cf3", "type": "ui_template", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "name": "Check Updates", "order": 18, "width": 6, "height": 1, "format": "Check updates", "storeOutMessages": false, "fwdInMessages": false, "resendOnRefresh": false, "templateScope": "local", "className": "", "x": 140, "y": 1420, "wires": [ [ "36b3b36c399ac7db" ] ] }, { "id": "f304680180a23479", "type": "ui_toast", "z": "017bd4e4a428bee5", "position": "dialog", "displayTime": "3", "highlight": "", "sendall": true, "outputs": 1, "ok": "OK", "cancel": "", "raw": true, "className": "", "topic": "", "name": "Info", "x": 630, "y": 120, "wires": [ [] ] }, { "id": "955f1e6794f368e2", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Scan", "group": "7aaf184330605300", "order": 1, "width": 6, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Files&Cloud

Current Status

--READY-- - everything is okay and ready to go :)

Routine-preparing - before starting the routine some time might pass depending on the number of photos

Routine-stopping - manually ending the routine by pressing the stop button

Routine-Photo X/Y - Showing the progress of the routine

No Camera Found - please check the camera ribbon cable

Error: XXX - Please contact info@openscan.eu or post an issue on Github.com

Projectname

Each photo set will be saved using the following pattern  YYYY-MM-DD_hh-mm-ss_projectname.zip (e.g. 2022-04-05_12.12.12_toysoldier.zip). Keep your files organized by giving each set a new projectname. If not specified 'default' will be used.

Rotor

Moving the rotor by increments of 5°. Please make sure to start the routine with the camera in the horizontal position.

Turntable

Moving the turntable by increments of 15°.

Ringlight

Use the ring light for shadow-free illumination. It is highly recommended to use the polarizer in order to avoid reflections. Note, that the polarizer will absorb 75% of the light, so you might need to use both ring lights.

Photos

Set the number of photos for the current set. 60-120 photos should be more than enough for most objects. If the reconstruction fails or is very bad with 60 photos, increasing the number of photos will not help!

Shutter

Again: Less is more! If the value is too high, some areas might get overexposed and thus, the software will not be able to recognize the surface feature of the object. Here are some reference values:

- no polarizer: 5-20ms

- mostly white object,  with polarizer + one ringlight: 50-200ms

Crop X/Y

Make sure to use the right object holder to place the object in the middle of the screen. Try to crop as many unnecessary areas as possible. This will greatly lower the file size and resulting transfer and reconstruction times!

Start/Stop

Use the buttons to start/stop the routine

Reboot/Shutdown

In case of an error, try to restart the device. Always use the shutdown button before powering-off the device!

MF - Manual Focus

By default, the switch is 'off', which means that autofocus is active. For small objects, it might be necessary to use manual focus: activate the switch and set the focus by pressing + and - accordingly. The distance is measured between the camera lens and the focal plane (which should be in the center or slightly in front of the center of the object).

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 470, "y": 200, "wires": [ [ "f304680180a23479" ] ] }, { "id": "dadf823225aa34c4", "type": "function", "z": "017bd4e4a428bee5", "name": "loadB", "func": "var file = 'turntable_mode'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\nif(data === '1' || data === 'True' || data === 'true'){\n data = true;\n}\nelse{\n data = false;\n}\nmsg.payload = data;\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 1620, "wires": [ [ "60f6ee795446200d" ] ] }, { "id": "60f6ee795446200d", "type": "ui_switch", "z": "017bd4e4a428bee5", "name": "", "label": "Turntable Mode", "tooltip": "", "group": "3b4bd36726be16d5", "order": 26, "width": 6, "height": 1, "passthru": true, "decouple": "false", "topic": "", "topicType": "str", "style": "", "onvalue": "true", "onvalueType": "bool", "onicon": "", "oncolor": "", "offvalue": "false", "offvalueType": "bool", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 320, "y": 1620, "wires": [ [ "227d89d897a8bdf5" ] ] }, { "id": "227d89d897a8bdf5", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'turntable_mode'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 1620, "wires": [ [] ] }, { "id": "8b317478e9d905b8", "type": "ui_button", "z": "017bd4e4a428bee5", "name": "Other Settings", "group": "3b4bd36726be16d5", "order": 24, "width": 2, "height": 1, "passthru": false, "label": "", "tooltip": "", "color": "", "bgcolor": "transparent", "className": "", "icon": "fa-question-circle", "payload": "

Network Settings

Hostname

The device can be accessed through any browser in the same network. Therefore, you can either enter the device's IP address or the given hostname. The standard name is 'openscan' but it is highly recommended to change the name, when using multiple devices (e.g. 'openscan1', 'openscan2' ...)

Select Wifi

After booting, the device will automatically search for available wireless networks and create a list. You can connect to a given network by entering the wifi password and country code. To find the right two-character country code, see the following list: ISO 3166 Country Code on Wikipedia

Search Wifi

You can manually refresh the list of available networks by pressing this button.

Reset Wifi

Delete the list of known wireless networks (and passwords) and reset the default. After this step, you will either need to use Ethernet or a modified wpa_supplicant.conf file. (see glennklockwood.com for more details about the wpa_supplicant.conf file, which has to be manually created and placed into the /boot/ directory of the sd-card)

", "payloadType": "str", "topic": "topic", "topicType": "msg", "x": 280, "y": 80, "wires": [ [] ] }, { "id": "814eaca62debe694", "type": "ui_text", "z": "017bd4e4a428bee5", "group": "3b4bd36726be16d5", "order": 23, "width": 4, "height": 1, "name": "", "label": "", "format": "OTHER SETTINGS", "layout": "col-center", "className": "", "x": 170, "y": 1580, "wires": [] }, { "id": "9df2481a03f24d0a", "type": "function", "z": "017bd4e4a428bee5", "name": "loadS", "func": "var file = 'update_type'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\ndata = fs.readFileSync(filepath+file, 'utf8');\n\nmsg.payload = String(data.replace(/(\\r\\n|\\n|\\r)/gm,\"\"));\nreturn msg", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 170, "y": 1660, "wires": [ [ "ecba1ecce99e3968" ] ] }, { "id": "f1de798f2a68e76e", "type": "function", "z": "017bd4e4a428bee5", "name": "write", "func": "var file = 'update_type'\nlet fs = global.get('fs');\nvar filepath = '/home/pi/OpenScan/settings/';\nconst content = String(msg.payload)\nfs.writeFile(filepath + file, content, err => {\n if (err) {\n return\n }\n });", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 470, "y": 1660, "wires": [ [] ] }, { "id": "ecba1ecce99e3968", "type": "ui_dropdown", "z": "017bd4e4a428bee5", "name": "Update_select", "label": "", "tooltip": "", "place": "Select version", "group": "3b4bd36726be16d5", "order": 19, "width": 0, "height": 0, "passthru": false, "multiple": false, "options": [ { "label": "Mini_simplified", "value": "mini", "type": "str" }, { "label": "Advanced", "value": "main", "type": "str" }, { "label": "Beta", "value": "beta", "type": "str" } ], "payload": "", "topic": "topic", "topicType": "msg", "className": "", "x": 320, "y": 1660, "wires": [ [ "f1de798f2a68e76e" ] ] } ] ================================================ FILE: update/mini/settings.js ================================================ /** * Node-RED Settings created at Mon, 24 Jan 2022 08:17:31 GMT * * It can contain any valid JavaScript code that will get run when Node-RED * is started. * * Lines that start with // are commented out. * Each entry should be separated from the entries above and below by a comma ',' * * For more information about individual settings, refer to the documentation: * https://nodered.org/docs/user-guide/runtime/configuration * * The settings are split into the following sections: * - Flow File and User Directory Settings * - Security * - Server Settings * - Runtime Settings * - Editor Settings * - Node Settings * **/ module.exports = { /******************************************************************************* * Flow File and User Directory Settings * - flowFile * - credentialSecret * - flowFilePretty * - userDir * - nodesDir ******************************************************************************/ /** The file containing the flows. If not set, defaults to flows_.json **/ flowFile: "flows.json", /** By default, credentials are encrypted in storage using a generated key. To * specify your own secret, set the following property. * If you want to disable encryption of credentials, set this property to false. * Note: once you set this property, do not change it - doing so will prevent * node-red from being able to decrypt your existing credentials and they will be * lost. */ credentialSecret: false, /** By default, the flow JSON will be formatted over multiple lines making * it easier to compare changes when using version control. * To disable pretty-printing of the JSON set the following property to false. */ flowFilePretty: true, /** By default, all user data is stored in a directory called `.node-red` under * the user's home directory. To use a different location, the following * property can be used */ //userDir: '/home/nol/.node-red/', userDir: '/home/pi/OpenScan/settings/.node-red/', /** Node-RED scans the `nodes` directory in the userDir to find local node files. * The following property can be used to specify an additional directory to scan. */ //nodesDir: '/home/nol/.node-red/nodes', /******************************************************************************* * Security * - adminAuth * - https * - httpsRefreshInterval * - requireHttps * - httpNodeAuth * - httpStaticAuth ******************************************************************************/ /** To password protect the Node-RED editor and admin API, the following * property can be used. See http://nodered.org/docs/security.html for details. */ //adminAuth: { // type: "credentials", // users: [{ // username: "admin", // password: "$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.", // permissions: "*" // }] //}, /** The following property can be used to enable HTTPS * This property can be either an object, containing both a (private) key * and a (public) certificate, or a function that returns such an object. * See http://nodejs.org/api/https.html#https_https_createserver_options_requestlistener * for details of its contents. */ /** Option 1: static object */ //https: { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') //}, /** Option 2: function that returns the HTTP configuration object */ // https: function() { // // This function should return the options object, or a Promise // // that resolves to the options object // return { // key: require("fs").readFileSync('privkey.pem'), // cert: require("fs").readFileSync('cert.pem') // } // }, /** If the `https` setting is a function, the following setting can be used * to set how often, in hours, the function will be called. That can be used * to refresh any certificates. */ //httpsRefreshInterval : 12, /** The following property can be used to cause insecure HTTP connections to * be redirected to HTTPS. */ //requireHttps: true, /** To password protect the node-defined HTTP endpoints (httpNodeRoot), * including node-red-dashboard, or the static content (httpStatic), the * following properties can be used. * The `pass` field is a bcrypt hash of the password. * See http://nodered.org/docs/security.html#generating-the-password-hash */ //httpNodeAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, //httpStaticAuth: {user:"user",pass:"$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN."}, /******************************************************************************* * Server Settings * - uiPort * - uiHost * - apiMaxLength * - httpServerOptions * - httpAdminRoot * - httpAdminMiddleware * - httpNodeRoot * - httpNodeCors * - httpNodeMiddleware * - httpStatic ******************************************************************************/ /** the tcp port that the Node-RED web server is listening on */ // uiPort: process.env.PORT || 1880, uiPort: process.env.PORT || 80, /** By default, the Node-RED UI accepts connections on all IPv4 interfaces. * To listen on all IPv6 addresses, set uiHost to "::", * The following property can be used to listen on a specific interface. For * example, the following would only allow connections from the local machine. */ //uiHost: "127.0.0.1", /** The maximum size of HTTP request that will be accepted by the runtime api. * Default: 5mb */ //apiMaxLength: '5mb', /** The following property can be used to pass custom options to the Express.js * server used by Node-RED. For a full list of available options, refer * to http://expressjs.com/en/api.html#app.settings.table */ //httpServerOptions: { }, /** By default, the Node-RED UI is available at http://localhost:1880/ * The following property can be used to specify a different root path. * If set to false, this is disabled. */ //httpAdminRoot: '/admin', httpAdminRoot: '/editor', /** The following property can be used to add a custom middleware function * in front of all admin http routes. For example, to set custom http * headers. It can be a single function or an array of middleware functions. */ // httpAdminMiddleware: function(req,res,next) { // // Set the X-Frame-Options header to limit where the editor // // can be embedded // //res.set('X-Frame-Options', 'sameorigin'); // next(); // }, /** Some nodes, such as HTTP In, can be used to listen for incoming http requests. * By default, these are served relative to '/'. The following property * can be used to specifiy a different root path. If set to false, this is * disabled. */ //httpNodeRoot: '/red-nodes', /** The following property can be used to configure cross-origin resource sharing * in the HTTP nodes. * See https://github.com/troygoode/node-cors#configuration-options for * details on its contents. The following is a basic permissive set of options: */ //httpNodeCors: { // origin: "*", // methods: "GET,PUT,POST,DELETE" //}, /** If you need to set an http proxy please set an environment variable * called http_proxy (or HTTP_PROXY) outside of Node-RED in the operating system. * For example - http_proxy=http://myproxy.com:8080 * (Setting it here will have no effect) * You may also specify no_proxy (or NO_PROXY) to supply a comma separated * list of domains to not proxy, eg - no_proxy=.acme.co,.acme.co.uk */ /** The following property can be used to add a custom middleware function * in front of all http in nodes. This allows custom authentication to be * applied to all http in nodes, or any other sort of common request processing. * It can be a single function or an array of middleware functions. */ //httpNodeMiddleware: function(req,res,next) { // // Handle/reject the request, or pass it on to the http in node by calling next(); // // Optionally skip our rawBodyParser by setting this to true; // //req.skipRawBodyParser = true; // next(); //}, /** When httpAdminRoot is used to move the UI to a different root path, the * following property can be used to identify a directory of static content * that should be served at http://localhost:1880/. */ //httpStatic: '/home/nol/node-red-static/', httpStatic: '/home/pi/OpenScan/', /******************************************************************************* * Runtime Settings * - lang * - logging * - contextStorage * - exportGlobalContextKeys * - externalModules ******************************************************************************/ /** Uncomment the following to run node-red in your preferred language. * Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko * Some languages are more complete than others. */ // lang: "de", /** Configure the logging output */ logging: { /** Only console logging is currently supported */ console: { /** Level of logging to be recorded. Options are: * fatal - only those errors which make the application unusable should be recorded * error - record errors which are deemed fatal for a particular request + fatal errors * warn - record problems which are non fatal + errors + fatal errors * info - record information about the general running of the application + warn + error + fatal errors * debug - record information which is more verbose than info + info + warn + error + fatal errors * trace - record very detailed logging + debug + info + warn + error + fatal errors * off - turn off all logging (doesn't affect metrics or audit) */ level: "info", /** Whether or not to include metric events in the log output */ metrics: false, /** Whether or not to include audit events in the log output */ audit: false } }, /** Context Storage * The following property can be used to enable context storage. The configuration * provided here will enable file-based context that flushes to disk every 30 seconds. * Refer to the documentation for further options: https://nodered.org/docs/api/context/ */ //contextStorage: { // default: { // module:"localfilesystem" // }, //}, /** `global.keys()` returns a list of all properties set in global context. * This allows them to be displayed in the Context Sidebar within the editor. * In some circumstances it is not desirable to expose them to the editor. The * following property can be used to hide any property set in `functionGlobalContext` * from being list by `global.keys()`. * By default, the property is set to false to avoid accidental exposure of * their values. Setting this to true will cause the keys to be listed. */ exportGlobalContextKeys: false, /** Configure how the runtime will handle external npm modules. * This covers: * - whether the editor will allow new node modules to be installed * - whether nodes, such as the Function node are allowed to have their * own dynamically configured dependencies. * The allow/denyList options can be used to limit what modules the runtime * will install/load. It can use '*' as a wildcard that matches anything. */ externalModules: { // autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */ // autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */ // palette: { /** Configuration for the Palette Manager */ // allowInstall: true, /** Enable the Palette Manager in the editor */ // allowUpload: true, /** Allow module tgz files to be uploaded and installed */ // allowList: [], // denyList: [] // }, // modules: { /** Configuration for node-specified modules */ // allowInstall: true, // allowList: [], // denyList: [] // } }, /******************************************************************************* * Editor Settings * - disableEditor * - editorTheme ******************************************************************************/ /** The following property can be used to disable the editor. The admin API * is not affected by this option. To disable both the editor and the admin * API, use either the httpRoot or httpAdminRoot properties */ //disableEditor: false, /** Customising the editor * See https://nodered.org/docs/user-guide/runtime/configuration#editor-themes * for all available options. */ editorTheme: { /** The following property can be used to set a custom theme for the editor. * See https://github.com/node-red-contrib-themes/theme-collection for * a collection of themes to chose from. */ //theme: "", palette: { /** The following property can be used to order the categories in the editor * palette. If a node's category is not in the list, the category will get * added to the end of the palette. * If not set, the following default order is used: */ //categories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], }, projects: { /** To enable the Projects feature, set this value to true */ enabled: false, workflow: { /** Set the default projects workflow mode. * - manual - you must manually commit changes * - auto - changes are automatically committed * This can be overridden per-user from the 'Git config' * section of 'User Settings' within the editor */ mode: "manual" } }, codeEditor: { /** Select the text editor component used by the editor. * Defaults to "ace", but can be set to "ace" or "monaco" */ lib: "ace", options: { /** The follow options only apply if the editor is set to "monaco" * * theme - must match the file name of a theme in * packages/node_modules/@node-red/editor-client/src/vendor/monaco/dist/theme * e.g. "tomorrow-night", "upstream-sunburst", "github", "my-theme" */ theme: "vs", /** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc. * for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html */ //fontSize: 14, //fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace", //fontLigatures: true, } } }, /******************************************************************************* * Node Settings * - fileWorkingDirectory * - functionGlobalContext * - functionExternalModules * - nodeMessageBufferMaxLength * - ui (for use with Node-RED Dashboard) * - debugUseColors * - debugMaxLength * - execMaxBufferSize * - httpRequestTimeout * - mqttReconnectTime * - serialReconnectTime * - socketReconnectTime * - socketTimeout * - tcpMsgQueueSize * - inboundWebSocketTimeout * - tlsConfigDisableLocalFiles * - webSocketNodeVerifyClient ******************************************************************************/ /** The working directory to handle relative file paths from within the File nodes * defaults to the working directory of the Node-RED process. */ //fileWorkingDirectory: "", /** Allow the Function node to load additional npm modules directly */ functionExternalModules: true, /** The following property can be used to set predefined values in Global Context. * This allows extra node modules to be made available with in Function node. * For example, the following: * functionGlobalContext: { os:require('os') } * will allow the `os` module to be accessed in a Function node using: * global.get("os") */ functionGlobalContext: { os:require('os'), path:require('path'), fs:require('fs'), }, /** The maximum number of messages nodes will buffer internally as part of their * operation. This applies across a range of nodes that operate on message sequences. * defaults to no limit. A value of 0 also means no limit is applied. */ //nodeMessageBufferMaxLength: 0, /** If you installed the optional node-red-dashboard you can set it's path * relative to httpNodeRoot * Other optional properties include * readOnly:{boolean}, * middleware:{function or array}, (req,res,next) - http middleware * ioMiddleware:{function or array}, (socket,next) - socket.io middleware */ //ui: { path: "ui" }, ui: { path: "" }, /** Colourise the console output of the debug node */ //debugUseColors: true, /** The maximum length, in characters, of any message sent to the debug sidebar tab */ debugMaxLength: 1000, /** Maximum buffer size for the exec node. Defaults to 10Mb */ //execMaxBufferSize: 10000000, /** Timeout in milliseconds for HTTP request connections. Defaults to 120s */ //httpRequestTimeout: 120000, /** Retry time in milliseconds for MQTT connections */ mqttReconnectTime: 15000, /** Retry time in milliseconds for Serial port connections */ serialReconnectTime: 15000, /** Retry time in milliseconds for TCP socket connections */ //socketReconnectTime: 10000, /** Timeout in milliseconds for TCP server socket connections. Defaults to no timeout */ //socketTimeout: 120000, /** Maximum number of messages to wait in queue while attempting to connect to TCP socket * defaults to 1000 */ //tcpMsgQueueSize: 2000, /** Timeout in milliseconds for inbound WebSocket connections that do not * match any configured node. Defaults to 5000 */ //inboundWebSocketTimeout: 5000, /** To disable the option for using local files for storing keys and * certificates in the TLS configuration node, set this to true. */ //tlsConfigDisableLocalFiles: true, /** The following property can be used to verify websocket connection attempts. * This allows, for example, the HTTP request headers to be checked to ensure * they include valid authentication information. */ //webSocketNodeVerifyClient: function(info) { // /** 'info' has three properties: // * - origin : the value in the Origin header // * - req : the HTTP request // * - secure : true if req.connection.authorized or req.connection.encrypted is set // * // * The function should return true if the connection should be accepted, false otherwise. // * // * Alternatively, if this function is defined to accept a second argument, callback, // * it can be used to verify the client asynchronously. // * The callback takes three arguments: // * - result : boolean, whether to accept the connection or not // * - code : if result is false, the HTTP error status to return // * - reason: if result is false, the HTTP reason string to return // */ //}, } ================================================ FILE: update/update.json ================================================ { "mini": { "1": { "src": "mini/fla.py", "dst": "/home/pi/OpenScan/files/fla.py", "filesize": 3910 }, "2": { "src": "mini/Arducam.py", "dst": "/usr/lib/python3/dist-packages/Arducam.py", "filesize": 6154 }, "3": { "src": "mini/OpenScan.py", "dst": "/usr/lib/python3/dist-packages/OpenScan.py", "filesize": 5864 }, "4": { "src": "mini/config.txt", "dst": "/boot/config.txt", "filesize": 2196 }, "5": { "src": "mini/flows.json", "dst": "/home/pi/OpenScan/settings/.node-red/flows.json", "filesize": 178895 }, "6": { "src": "mini/settings.js", "dst": "/root/.node-red/settings.js", "filesize": 20321 }, "7": { "src": "files/logo2.jpg", "dst": "/home/pi/OpenScan/files/logo.jpg", "filesize": 582519 } }, "betaArdu": { "1": { "src": "betaArdu/fla.py", "dst": "/home/pi/OpenScan/files/fla.py", "filesize": 12089 }, "2": { "src": "betaArdu/OpenScan.py", "dst": "/usr/lib/python3/dist-packages/OpenScan.py", "filesize": 10253 }, "3": { "src": "betaArdu/config.txt", "dst": "/boot/config.txt", "filesize": 2185 }, "4": { "src": "betaArdu/flows.json", "dst": "/home/pi/OpenScan/settings/.node-red/flows.json", "filesize": 315147 }, "5": { "src": "betaArdu/settings.js", "dst": "/root/.node-red/settings.js", "filesize": 21199 } }, "main": { "1": { "src": "main/fla.py", "dst": "/home/pi/OpenScan/files/fla.py", "filesize": 5445 }, "2": { "src": "main/Arducam.py", "dst": "/usr/lib/python3/dist-packages/Arducam.py", "filesize": 6154 }, "3": { "src": "main/OpenScan.py", "dst": "/usr/lib/python3/dist-packages/OpenScan.py", "filesize": 6161 }, "4": { "src": "main/config.txt", "dst": "/boot/config.txt", "filesize": 2179 }, "5": { "src": "main/flows.json", "dst": "/home/pi/OpenScan/settings/.node-red/flows.json", "filesize": 360661 }, "6": { "src": "main/settings.js", "dst": "/root/.node-red/settings.js", "filesize": 20321 }, "7": { "src": "files/logo.jpg", "dst": "/home/pi/OpenScan/files/logo.jpg", "filesize": 582519 } }, "beta": { "1": { "src": "beta/fla.py", "dst": "/home/pi/OpenScan/files/fla.py", "filesize": 5366 }, "2": { "src": "beta/Arducam.py", "dst": "/usr/lib/python3/dist-packages/Arducam.py", "filesize": 6154 }, "3": { "src": "beta/OpenScan.py", "dst": "/usr/lib/python3/dist-packages/OpenScan.py", "filesize": 6443 }, "4": { "src": "beta/config.txt", "dst": "/boot/config.txt", "filesize": 2179 }, "5": { "src": "beta/flows.json", "dst": "/home/pi/OpenScan/settings/.node-red/flows.json", "filesize": 354569 }, "6": { "src": "beta/settings.js", "dst": "/root/.node-red/settings.js", "filesize": 20321 }, "7": { "src": "files/logo.jpg", "dst": "/home/pi/OpenScan/files/logo.jpg", "filesize": 582519 } } }