Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29418.71
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ArduinoProps", "ArduinoProps.vcxitems", "{45155B06-E8AA-4EA0-8802-94310DB08612}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {2F208AC8-057F-45ED-BFFE-924E36BD9E8D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
<HasSharedItems>true</HasSharedItems>
|
||||
<ItemsProjectGuid>{45155b06-e8aa-4ea0-8802-94310db08612}</ItemsProjectGuid>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ProjectCapability Include="SourceItemsFromImports" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="$(MSBuildThisFileDirectory)keywords.txt" />
|
||||
<Text Include="$(MSBuildThisFileDirectory)LICENSE.txt" />
|
||||
<Text Include="$(MSBuildThisFileDirectory)library.properties" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)EXAMPLES.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnBridgeProps\BlinkOnBridgeProps.ino" />
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnBridgePubSub\BlinkOnBridgePubSub.ino" />
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnEthernetProps\BlinkOnEthernetProps.ino" />
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnWifiProps\BlinkOnWifiProps.ino" />
|
||||
<None Include="$(MSBuildThisFileDirectory)LICENSE.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)Props.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)PropsAction.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)PropsData.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)PROPS_FOR_ESCAPE_ROOM_2_0.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)README.md" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\ArduinoProps.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\BridgeProps.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\EthernetProps.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\Props.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\PropsAction.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\PropsData.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\WifiProps.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\ArduinoProps.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\BridgeProps.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\EthernetProps.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\Props.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\PropsAction.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\PropsData.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\WifiProps.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="$(MSBuildThisFileDirectory)examples-shot.png" />
|
||||
<Image Include="$(MSBuildThisFileDirectory)install-shot.png" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;s</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Examples">
|
||||
<UniqueIdentifier>{a17437ae-fae9-404e-b013-d8b8094f7732}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="$(MSBuildThisFileDirectory)library.properties" />
|
||||
<Text Include="$(MSBuildThisFileDirectory)keywords.txt" />
|
||||
<Text Include="$(MSBuildThisFileDirectory)LICENSE.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)LICENSE.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)PROPS_FOR_ESCAPE_ROOM_2_0.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)README.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)Props.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)PropsAction.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)PropsData.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)EXAMPLES.md" />
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnBridgeProps\BlinkOnBridgeProps.ino">
|
||||
<Filter>Examples</Filter>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnBridgePubSub\BlinkOnBridgePubSub.ino">
|
||||
<Filter>Examples</Filter>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnEthernetProps\BlinkOnEthernetProps.ino">
|
||||
<Filter>Examples</Filter>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)examples\BlinkOnWifiProps\BlinkOnWifiProps.ino">
|
||||
<Filter>Examples</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\ArduinoProps.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\BridgeProps.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\EthernetProps.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\Props.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\PropsAction.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\PropsData.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)src\WifiProps.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\ArduinoProps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\BridgeProps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\EthernetProps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\Props.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\PropsAction.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\PropsData.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)src\WifiProps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="$(MSBuildThisFileDirectory)examples-shot.png" />
|
||||
<Image Include="$(MSBuildThisFileDirectory)install-shot.png" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+409
@@ -0,0 +1,409 @@
|
||||
# ArduinoProps library examples
|
||||
An adaptation of the internal led Blink example (https://www.arduino.cc/en/tutorial/blink) as a simple MQTT props.
|
||||
|
||||
1. **BlinkOnBridgeProps**: the Blink example on a Yun props with *ArduinoProps library*
|
||||
2. **BlinkOnEthernetProps**: the Blink example on an Ethernet props with *ArduinoProps library*
|
||||
3. **BlinkOnWifiProps**: the Blink example on a Wifi props with *ArduinoProps library*
|
||||
4. **BlinkOnBridgePubSub**: the Blink example on props using *PubSubClient* directly
|
||||
|
||||
MQTT messages are received asynchronously therefore to keep the sketch responsive to MQTT commands, calls to delay() should be avoided (except short ones, say < 100milliseconds).
|
||||
|
||||
Asynchronous-like behavior is obtained using *PropsAction*, *TimedAction* or *VariableTimedAction*.
|
||||
|
||||
*Copy and change any of these sketches to build your own Arduino connected props, you will only be limited by your imagination.*
|
||||
|
||||
|
||||
## 1. *BlinkOnBridgeProps*: the Blink example on a Yun props with *ArduinoProps library*
|
||||
|
||||
With *Props* class, the code is the most readable. All MQTT stuff are hidden in *Props* calls, your processing code is easier to write and maintain.
|
||||
|
||||
```csharp
|
||||
/* BlinkOnBridgeProps.ino
|
||||
MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Adapt the Blink example (https://www.arduino.cc/en/tutorial/blink) as a
|
||||
simple MQTT props. Avoid delay() calls (except short ones) in loop() to
|
||||
ensure CPU for MQTT protocol. Use PropsAction checks instead.
|
||||
|
||||
Copy and change it to build your fist Arduino connected props, you will
|
||||
only be limited by your imagination.
|
||||
|
||||
Requirements: install ArduinoProps.zip library.
|
||||
*/
|
||||
#include <Bridge.h>
|
||||
#include "ArduinoProps.h"
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// prpos inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
|
||||
BridgeProps props(u8"Arduino Contrôleur", // as MQTT client id, should be unique per client for given broker
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/inbox",
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/outbox",
|
||||
"192.168.1.42", // your MQTT server IP address
|
||||
1883); // your MQTT server port;
|
||||
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
PropsDataLogical led(u8"led");
|
||||
|
||||
void clignote(); // forward
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Bridge.begin();
|
||||
//updateBrokerAdressFromFile("/root/broker", &props); // if you're running our Escape Room control software (Room 2.0)
|
||||
|
||||
props.addData(&clignoter);
|
||||
props.addData(&led);
|
||||
|
||||
props.begin(InboxMessage::run);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output
|
||||
|
||||
// At this point, the broker is not connected yet
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
props.loop();
|
||||
|
||||
led.setValue(digitalRead(LED_BUILTIN)); // read I/O
|
||||
|
||||
clignoteAction.check(); // do your stuff, don't freeze the loop with delay() calls
|
||||
}
|
||||
|
||||
void clignote()
|
||||
{
|
||||
if (clignoter.value()) {
|
||||
led.setValue(!led.value());
|
||||
digitalWrite(LED_BUILTIN, led.value() ? HIGH : LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void InboxMessage::run(String a) {
|
||||
|
||||
if (a == u8"app:startup")
|
||||
{
|
||||
props.sendAllData();
|
||||
props.sendDone(a);
|
||||
}
|
||||
else if (a == "clignoter:1")
|
||||
{
|
||||
clignoter.setValue(true);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else if (a == "clignoter:0")
|
||||
{
|
||||
clignoter.setValue(false);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge omition of the props command
|
||||
props.sendOmit(a);
|
||||
}
|
||||
}
|
||||
|
||||
void updateBrokerAdressFromFile(const char* broker_file, BridgeProps* props)
|
||||
{
|
||||
// broker IP address is stored in Linino file systems and updated with ssh command by Room 2.0
|
||||
IPAddress ip;
|
||||
|
||||
Process _process;
|
||||
_process.begin("cat");
|
||||
_process.addParameter(broker_file); // for ssh remotely set broker address
|
||||
_process.run(); // run the process and wait for its termination
|
||||
String b;
|
||||
while (_process.available() > 0) {
|
||||
char c = _process.read();
|
||||
b += c;
|
||||
}
|
||||
b.trim();
|
||||
|
||||
if (ip.fromString(b.c_str())) props->setBrokerIpAddress(ip);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Memory imprint on Arduino Yun:
|
||||
```bash
|
||||
The sketch uses 16720 bytes (58%) of the program storage space. The maximum is 28672 bytes.
|
||||
Global variables use 1122 bytes (43%) of dynamic memory, which leaves 1438 bytes for local variables. The maximum is 2560 bytes.
|
||||
|
||||
```
|
||||
|
||||
### Memory imprint on *Dragino Yun + Arduino Mega 2560:
|
||||
```bash
|
||||
The sketch uses 14030 bytes (5%) of the program storage space. The maximum is 258048 bytes.
|
||||
Global variables use 982 bytes (11%) of dynamic memory, which leaves 7210 bytes for local variables. The maximum is 8192 bytes.
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 2. *BlinkOnEthernetProps*: the Blink example on an Ethernet props with *ArduinoProps library*
|
||||
|
||||
Sketch with *EthernetProps* differs slightly from code with *BridgeProps*.
|
||||
|
||||
The library comes with a number of example sketches. See **File > Examples > ArduinoProps** within the Arduino IDE application after installing the *ArduinoProps* library from the `.zip` file.
|
||||
|
||||

|
||||
|
||||
## 3. *BlinkOnWifiProps*: the Blink example on a Wifi props with *ArduinoProps library*
|
||||
|
||||
Sketch with *WifiProps* differs slightly from code with *BridgeProps*.
|
||||
|
||||
|
||||
## 4. *BlinkOnBridgePubSub*: the Blink example on props using *PubSubClient* directly
|
||||
|
||||
Using *PubSubClient* directly does not save much memory and makes the sketch code less readable, the processing code will be a bit lost in the MQTT code
|
||||
|
||||
However, this can help in special cases.
|
||||
|
||||
```csharp
|
||||
/*
|
||||
Name: BlinkOnBridgePubSub.ino
|
||||
Created: 29/10/2019 16:51:55
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Adapt the Blink example (https://www.arduino.cc/en/tutorial/blink) as a
|
||||
simple MQTT props with PubSubClient.
|
||||
*/
|
||||
#include <Bridge.h>
|
||||
#include <BridgeClient.h>
|
||||
#include <Process.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <VariableTimedAction.h>
|
||||
|
||||
#define BROKER "192.168.1.42" // your MQTT server IP address
|
||||
#define PROPS_NAME u8"Arduino Contrôleur" // as MQTT client id, should be unique per client for given broker
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// props inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://github.com/fauresystems/escape-room#room-control-software
|
||||
#define PROPS_INBOX u8"Room/Demoniak/Props/Arduino Contrôleur/inbox"
|
||||
#define PROPS_OUTBOX u8"Room/Demoniak/Props/Arduino Contrôleur/outbox"
|
||||
|
||||
// Yun can store broker IP address in Linino file systems, and updatred with ssh command
|
||||
#define YUN_BROKER_FILE "/root/broker"
|
||||
|
||||
void publishAll(); // forward
|
||||
void publishChanges(); // forward
|
||||
|
||||
class Blinking : public VariableTimedAction {
|
||||
private:
|
||||
//this method will be called at your specified interval
|
||||
unsigned long run() {
|
||||
//swicth LED if blinking mode
|
||||
if (blink) {
|
||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||
}
|
||||
|
||||
//return code of 0 indicates no change to the interval
|
||||
//if the interval must be changed, then return the new interval
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
bool blink;
|
||||
};
|
||||
|
||||
class PublishAllData : public VariableTimedAction {
|
||||
private:
|
||||
unsigned long run() {
|
||||
publishAll();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class PublishChangedData : public VariableTimedAction {
|
||||
private:
|
||||
unsigned long run() {
|
||||
publishChanges();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
Blinking blinking;
|
||||
|
||||
bool led_ref;
|
||||
bool blink_ref;
|
||||
|
||||
BridgeClient _ethClient;
|
||||
PubSubClient _client(_ethClient);
|
||||
IPAddress _brokerAddress;
|
||||
|
||||
void callback(char*, byte*, unsigned int); // forward
|
||||
|
||||
// MQTT
|
||||
unsigned long lastReconnection(0L);
|
||||
PublishAllData publishAllData;
|
||||
PublishChangedData publishChangedData;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Bridge.begin();
|
||||
|
||||
Process p;
|
||||
p.begin("cat");
|
||||
p.addParameter(YUN_BROKER_FILE);
|
||||
p.run(); // run the process and wait for its termination
|
||||
String b;
|
||||
while (p.available() > 0) {
|
||||
char c = p.read();
|
||||
b += c;
|
||||
}
|
||||
b.trim();
|
||||
|
||||
if (!_brokerAddress.fromString(b.c_str())) _brokerAddress.fromString(BROKER);
|
||||
_client.setServer(_brokerAddress, 1883);
|
||||
_client.setCallback(callback);
|
||||
|
||||
publishAllData.start(30000);
|
||||
publishChangedData.start(400);
|
||||
|
||||
blinking.blink = true;
|
||||
blinking.start(1000);
|
||||
blink_ref = false;
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
led_ref = LOW;
|
||||
|
||||
// At this point, the broker is not connected yet
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (_client.connected())
|
||||
{
|
||||
_client.loop();
|
||||
}
|
||||
else if (millis() > lastReconnection)
|
||||
{
|
||||
lastReconnection += 5000L;
|
||||
|
||||
if (_client.connect(PROPS_NAME, PROPS_OUTBOX, 2, true, "DISCONNECTED"))
|
||||
{
|
||||
_client.publish(PROPS_OUTBOX, "CONNECTED", true);
|
||||
_client.subscribe(PROPS_INBOX, 1); // max QoS is 1 for PubSubClient subsciption
|
||||
lastReconnection = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
// automation code should be here (nothing to do for this example)
|
||||
|
||||
VariableTimedAction::updateActions();
|
||||
}
|
||||
|
||||
void publishAll()
|
||||
{
|
||||
String buf = "DATA";
|
||||
|
||||
bool led = digitalRead(LED_BUILTIN);
|
||||
|
||||
buf += u8" led=" + (led ? String("1") : String("0"));
|
||||
led_ref = led;
|
||||
|
||||
buf += u8" clignote=" + (blinking.blink ? String("oui") : String("non"));
|
||||
blink_ref = blinking.blink;
|
||||
|
||||
_client.publish(PROPS_OUTBOX, buf.c_str());
|
||||
}
|
||||
|
||||
void publishChanges()
|
||||
{
|
||||
String buf = "DATA";
|
||||
|
||||
bool led = digitalRead(LED_BUILTIN);
|
||||
|
||||
if (led != led_ref)
|
||||
{
|
||||
buf += u8" led=" + (led ? String("1") : String("0"));
|
||||
led_ref = led;
|
||||
}
|
||||
|
||||
if (blinking.blink != blink_ref)
|
||||
{
|
||||
buf += u8" clignote=" + (blinking.blink ? String("oui") : String("non"));
|
||||
blink_ref = blinking.blink;
|
||||
}
|
||||
|
||||
if (buf.length() > 4)
|
||||
_client.publish(PROPS_OUTBOX, buf.c_str());
|
||||
}
|
||||
|
||||
void publishDone(String a)
|
||||
{
|
||||
a = "DONE " + a;
|
||||
_client.publish(PROPS_OUTBOX, a.c_str());
|
||||
}
|
||||
|
||||
void onInboxMessage(String a) {
|
||||
|
||||
if (a == u8"app:startup")
|
||||
{
|
||||
publishAll();
|
||||
publishDone(a);
|
||||
}
|
||||
else if (a == "clignoter:1")
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
blinking.blink = true;
|
||||
|
||||
publishAll(); // all data change, we don't have to be selctive then
|
||||
publishDone(a); // acknowledge props command action
|
||||
}
|
||||
else if (a == "clignoter:0")
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
blinking.blink = false;
|
||||
|
||||
publishAll(); // all data change, we don't have to be selctive then
|
||||
publishDone(a); // acknowledge props command action
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge omition of the props command
|
||||
_client.publish(PROPS_OUTBOX, String("OMIT " + a).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int len)
|
||||
{
|
||||
if (len)
|
||||
{
|
||||
char* p = (char*)malloc(len + 1);
|
||||
memcpy(p, payload, len);
|
||||
p[len] = '\0';
|
||||
if (String(p) == "@PING")
|
||||
_client.publish(PROPS_OUTBOX, "PONG");
|
||||
else
|
||||
onInboxMessage(p);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Memory imprint on Arduino Yun:
|
||||
```bash
|
||||
The sketch uses 15930 bytes (55%) of the program storage space. The maximum is 28672 bytes.
|
||||
Global variables use 1013 bytes (39%) of dynamic memory, which leaves 1547 bytes for local variables. The maximum is 2560 bytes.
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
**Marie FAURE** (Oct 18th, 2019)
|
||||
* company: FAURE SYSTEMS SAS
|
||||
* mail: *dev at faure dot systems*
|
||||
* github: <a href="https://github.com/fauresystems?tab=repositories" target="_blank">fauresystems</a>
|
||||
* web: <a href="https://www.live-escape.net/" target="_blank">Live Escape Grenoble</a>
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 fauresystems
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
## MIT License
|
||||
|
||||
*Copyright (c) 2019 Marie Faure \<dev at faure dot systems\>*
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2019 Marie Faure
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Arduino Props for Escape Room 2.0
|
||||
|
||||
The *ArduinoProps library* for Arduino has been designed to build *escape room 2.0 props*.
|
||||
|
||||
Specificities of props for escape rooms:
|
||||
- very high uptime (near 100%)
|
||||
- fast problem fixing
|
||||
- real-time (human scale) notification and control
|
||||
- fallback controls
|
||||
- low Total Cost of Ownership
|
||||
|
||||
Benefits and specificities of the *ArduinoProps library*
|
||||
- rely on standard MQTT messaging protocol for IoT
|
||||
- simplify and ease Arduino coding
|
||||
- constant sustaining of Arduino code (YUN)
|
||||
- real-time notification, monitoring and control (human scale)
|
||||
|
||||
# Escape room 2.0 constraints on props
|
||||
Escape game are opened 7 days a week and particularly booked during weekends and usually until midnight. Therefore their uptime must be close to 100% and incidents must be worked-around immediately by the game master (manual commands).
|
||||
|
||||
Escape Room Props operation and performance impact directly escape game customer experience. Therefore notifications must be processed in human-scale real-time, automatically or manually by a non-technological user (the game master).
|
||||
|
||||
Escape Room Props TCO (Total Cost of Ownership) must be affordable by escape games (SOHO costs for small businesses), processing is usually supported by Arduino or Raspberry boards. IT professional 24/7 support must be avoided for cost reasons. Therefore props code must be the most reliable.
|
||||
|
||||
|
||||
# *ArduinoProps library* for escape room 2.0
|
||||
[MQTT](http://mqtt.org/) is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport.
|
||||
|
||||
*ArduinoProps library* relies on *[Nick O'Leary PubSubClient](https://github.com/knolleary/pubsubclient)*, a simple publish/subscribe messaging Arduino library. [PubSubClient](https://github.com/knolleary/pubsubclient) is a MQTT client with a few limitations:
|
||||
|
||||
- It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
|
||||
- The maximum message size, including header, is 128 bytes by default. This is configurable via MQTT_MAX_PACKET_SIZE in PubSubClient.h.
|
||||
- The keepalive interval is set to 15 seconds by default. This is configurable via MQTT_KEEPALIVE in PubSubClient.h.
|
||||
- The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by changing value of MQTT_VERSION in PubSubClient.h.
|
||||
|
||||
How does *ArduinoProps library* use MQTT efficiently in escape rooms:
|
||||
|
||||
- Every props has two MQTT topics assigned as inbox and outbox.
|
||||
- More MQTT topics can be used to share game countdown, scenario or any common useful dynamic data for the escape game.
|
||||
- Notifying the props is done by publishing a message on its inbox topic.
|
||||
- Other props of software monitor can subscribe to the props outbox topic to receive all its notifications.
|
||||
- *ArduinoProps library* includes a ping process at application level, in LAN network the round-trip-delay is around 50 milliseconds.
|
||||
- *ArduinoProps library* is defaulted to publish QoS 0 messages and to subscribe at QoS 1.
|
||||
|
||||
*ArduinoProps library* wraps [PubSubClient](https://github.com/knolleary/pubsubclient) to make Arduino code the most simple and readable for better sustainability.
|
||||
|
||||
# Arduino YUN and YUN shields
|
||||
Arduino YUN is a Arduino with Leonardo MCU/GPIO and Linux SoC.
|
||||
|
||||
Arduino YUN shield and Dragino YUN shield are YUN Linux SoC to be plugged onto Arduino Uno, Mega or Due.
|
||||
|
||||
**Arduino YUN advantage is that you can upload AVR program from the Arduino IDE at any moment during the Props lifetime**:
|
||||
- change sensor settings when mechanics is undermined
|
||||
- tune whole challenge processing
|
||||
|
||||
Arduino YUN can be rebooted or its MCU resetted remotely with simple SSH commands.
|
||||
|
||||
Moreover, SSH command can be used to remotely change the MQTT server IP address, *ArduinoProps library* read this address at sketch start.
|
||||
|
||||
Therefore, Arduino YUN or YUN shield is the preferred solution for props with Arduino boards because it will be very easy to sustain over time and greatly facilitates fallback plans to ensure maximum uptime.
|
||||
|
||||
# Arduino Ethernet and Wifi
|
||||
Arduino with Ethernet or Wifi shield is suitable to build an Escape Room Props, however the sketch must be uploaded with an USB cable, so it is not convenient for props design and worse for props in production.
|
||||
|
||||
Therefore I strongly advise Arduino YUN or YUN shield for Escape Room props.
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
**Marie FAURE** (Oct 18th, 2019)
|
||||
* company: FAURE SYSTEMS SAS
|
||||
* mail: *dev at faure dot systems*
|
||||
* github: <a href="https://github.com/fauresystems?tab=repositories" target="_blank">fauresystems</a>
|
||||
* web: <a href="https://www.live-escape.net/" target="_blank">Live Escape Grenoble</a>
|
||||
@@ -0,0 +1,326 @@
|
||||
# *Props* class reference
|
||||
|
||||
*Props* is a base class for 3 connected props, depending on their network interface:
|
||||
* ***BridgeProps*** for Yun board and Yun shield
|
||||
* ***EthernetProps*** for Ethernet shield
|
||||
* ***WifiProps*** for Wifi shield
|
||||
|
||||
*Props* class reference:
|
||||
1. Define the *Props*
|
||||
* Defining a *BridgeProps*
|
||||
* Defining an *EthernetProps*
|
||||
* Defining a *WifiProps*
|
||||
2. Setup the *Props*
|
||||
* Change timings
|
||||
3. Call in sketch *loop()*
|
||||
4. Implement `InboxMessage::run()` callback
|
||||
5. Update broker address from SSH (*BridgeProps*)
|
||||
6. Examples
|
||||
|
||||
|
||||
## 1. Defines the *Props*
|
||||
Defining a Bridge (Yun), Ethernet or Wifi *Props* is slighlty different.
|
||||
|
||||
### Defining a *BridgeProps*
|
||||
```csharp
|
||||
#include <Bridge.h>
|
||||
#include "ArduinoProps.h"
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// prpos inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
|
||||
BridgeProps props(u8"Arduino Contrôleur", // as MQTT client id, should be unique per client for given broker
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/inbox",
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/outbox",
|
||||
"192.168.1.42", // your MQTT server IP address
|
||||
1883); // your MQTT server port;
|
||||
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
PropsDataLogical led(u8"led");
|
||||
|
||||
void clignote(); // forward
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote);
|
||||
```
|
||||
Create *BridgeProps* instance with MQTT parameters, then create *PropsData* instances and *PropsActions*.
|
||||
|
||||
The *Bridge* instance is created by `#include <Bridge.h>`.
|
||||
|
||||
### Defining an *EthernetProps*
|
||||
```csharp
|
||||
#include <Ethernet.h>
|
||||
#include <IPAddress.h>
|
||||
#include "ArduinoProps.h"
|
||||
|
||||
byte mac[] = { 0xFC, 0xFA, 0xEA, 0x00, 0x00, 0x02 }; //<<< MAKE SURE IT'S UNIQUE IN YOUR NETWORK!!!
|
||||
String ip = "192.168.1.19"; //<<< ENTER YOUR IP ADDRESS HERE ("" for DHCP)
|
||||
|
||||
// Builtin led is not available with the shield
|
||||
#undef LED_BUILTIN
|
||||
#define LED_BUILTIN 8
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// prpos inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
|
||||
EthernetProps props(u8"Arduino Contrôleur", // as MQTT client id, should be unique per client for given broker
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/inbox",
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/outbox",
|
||||
"192.168.1.42", // your MQTT server IP address
|
||||
1883); // your MQTT server port;
|
||||
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
PropsDataLogical led(u8"led");
|
||||
|
||||
void clignote(); // forward
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote);
|
||||
```
|
||||
Create MAC address (which must be unique, it's the hardware ID in the netwprk).
|
||||
|
||||
Create *EthernetProps* instance with MQTT parameters, then create *PropsData* instances and *PropsActions*.
|
||||
|
||||
The *Ethernet* instance is created by `#include <Ethernet.h>`.
|
||||
|
||||
### Defining a *WifiProps*
|
||||
```csharp
|
||||
#include "ArduinoProps.h"
|
||||
|
||||
// Setup your WiFi network
|
||||
const char* ssid = "";
|
||||
const char *passphrase = "";
|
||||
|
||||
// Builtin led is not available with the shield
|
||||
#undef LED_BUILTIN
|
||||
#define LED_BUILTIN 8
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// prpos inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
|
||||
WifiProps props(u8"Arduino Contrôleur", // as MQTT client id, should be unique per client for given broker
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/inbox",
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/outbox",
|
||||
"192.168.1.42", // your MQTT server IP address
|
||||
1883); // your MQTT server port;
|
||||
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
PropsDataLogical led(u8"led");
|
||||
|
||||
void clignote(); // forward
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote);
|
||||
```
|
||||
The *AduinoProps* library uses **<a href="https://github.com/arduino-libraries/WiFiNINA" target="_blank">WiFiNINA</a>** WiFi library for **Arduino Uno WiFi Rev 2**, **Arduino NANO 33 IoT**, **Arduino MKR 1010** and **Arduino MKR VIDOR 4000**.
|
||||
|
||||
Create *WifiProps* instance with MQTT parameters, then create *PropsData* instances and *PropsActions*.
|
||||
|
||||
#### MQTT topics
|
||||
The props listens to the *inbox* topic for command messages and send data and messages into the *outbox* topic.
|
||||
|
||||
If you're running Live Escape Room 2.0 control software, you have to respect *inbox*/*outbox* syntax:
|
||||
|
||||
Room 2.0 *inbox* topic:
|
||||
```csharp
|
||||
format: Room/[escape room name]/Props/[props name]/inbox
|
||||
example: u8"Room/Demoniak/Props/Arduino Contrôleur/inbox"
|
||||
```
|
||||
Room 2.0 *outbox* topic:
|
||||
```csharp
|
||||
format: Room/[escape room name]/Props/[props name]/outbox
|
||||
example: u8"Room/Demoniak/Props/Arduino Contrôleur/outbox"
|
||||
```
|
||||
See [README.md](README.md): **4. Application protocol for escape room 2.0 props**.
|
||||
|
||||
#### *PropsData* instances
|
||||
*PropsData* provides a facility to monitor data sent into MQTT outbox.
|
||||
|
||||
See [PropsData.md](PropsData.md) for *PropsData* reference.
|
||||
|
||||
#### *PropsAction* instances
|
||||
To maintain a non-blocking asynchronous behavior of your props, use *PropsAction* objects to trigger action periodically.
|
||||
|
||||
Reading I/O and simple computing (arithmetics) can be done at every sketch loop but long computing have to run periodically.
|
||||
|
||||
For example, to blink a led, don't block the skecth with a `delay(1000)` call to switch the output: instead, ***check if 1 second is elapsed to switch the output***.
|
||||
|
||||
See [PropsAction.md](PropsData.md) for *PropsAction* reference.
|
||||
|
||||
|
||||
## 2. Setup the *Props*
|
||||
`InboxMessage::run` is the callback executed when a message is received in the MQTT inbox.
|
||||
```csharp
|
||||
void setup()
|
||||
{
|
||||
// CODE TO BEGIN THE INTERNET CONNECTION
|
||||
... see below the different code for BridgeProps, EthenetProps and WifiProps
|
||||
|
||||
// SAME CODE FOR ALL PROPS
|
||||
props.addData(&clignoter);
|
||||
props.addData(&led);
|
||||
|
||||
props.begin(InboxMessage::run);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output
|
||||
|
||||
// At this point, the broker is not connected yet
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
*BridgeProps*, *EthenetProps* and *WifiProps* has the exactly same setup:
|
||||
|
||||
- add *PropsData* references to the props
|
||||
- call `props.begin(InboxMessage::run)`
|
||||
|
||||
The setup code is slighlty different to start the network client (bridge, ethernet of wifi):
|
||||
```csharp
|
||||
void setup()
|
||||
{
|
||||
#if defined(BRIDGEPROPS_H)
|
||||
Bridge.begin();
|
||||
updateBrokerAdressFromFile("/root/broker", &props); // if you're running our Escape Room control software (Room 2.0)
|
||||
|
||||
#elif defined(ETHERNETPROPS_H)
|
||||
// can do more static IP configuration
|
||||
//Ethernet.setSubnetMask(IPAddress());
|
||||
//Ethernet.setGatewayIP(IPAddress());
|
||||
//Ethernet.setDnsServerIP(IPAddress());
|
||||
|
||||
IPAddress ipa;
|
||||
if (!ipa.fromString(ip)) {
|
||||
// attempt a DHCP connection:
|
||||
if (!Ethernet.begin(mac)) {
|
||||
// if DHCP fails, start with a hard-coded address:
|
||||
Ethernet.begin(mac, IPAddress(10, 90, 90, 239));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Ethernet.begin(mac, ipa);
|
||||
}
|
||||
delay(1500); // time for shield stuff
|
||||
|
||||
#elif defined(WIFIPROPS_H)
|
||||
// can do more static IP configuration
|
||||
//WiFi.config(IPAddress(), IPAddress(), IPAddress());
|
||||
//WiFi.config(IPAddress(), IPAddress());
|
||||
//WiFi.config(IPAddress());
|
||||
|
||||
WiFi.begin(ssid, passphrase);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
}
|
||||
#endif
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Change timings
|
||||
Default timings are:
|
||||
* check data changes every 400 milliseconds
|
||||
* *Props* remains silent for up to 30 seconds
|
||||
|
||||
Timings can be changed anywhere in the code with `resetIntervals()`:
|
||||
```csharp
|
||||
props.resetIntervals(250, 15); // check data changes every 250 milliseconds, silent up to 15 seconds
|
||||
```
|
||||
|
||||
|
||||
## 3. Call in sketch *loop()*
|
||||
```csharp
|
||||
void loop()
|
||||
{
|
||||
props.loop();
|
||||
|
||||
led.setValue(digitalRead(LED_BUILTIN)); // read I/O
|
||||
|
||||
clignoteAction.check(); // do your stuff, don't freeze the loop with delay() calls
|
||||
}
|
||||
|
||||
```
|
||||
Couldn't be more simple!
|
||||
|
||||
Just call `props.loop()` when entering the sketch `loop()`, it will maintain MQTT broker connection and run callback when receiving MQTT messages.
|
||||
|
||||
|
||||
## 4. Implement `InboxMessage::run()` callback
|
||||
`InboxMessage::run` is the callback executed when a message is received in the MQTT inbox, you have to implement it.
|
||||
|
||||
Example for Room 2.0 commands:
|
||||
```csharp
|
||||
void InboxMessage::run(String a) {
|
||||
|
||||
if (a == u8"app:startup")
|
||||
{
|
||||
props.sendAllData();
|
||||
props.sendDone(a);
|
||||
}
|
||||
else if (a == "clignoter:1")
|
||||
{
|
||||
clignoter.setValue(true);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else if (a == "clignoter:0")
|
||||
{
|
||||
clignoter.setValue(false);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge omition of the props command
|
||||
props.sendOmit(a);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 5. Update broker address from SSH (*BridgeProps*)
|
||||
Room 2.0 use a SSH command to change remotely the broker IP address (in /root/broker file) and to relaunch to sketch:
|
||||
```bash
|
||||
ssh 'echo 192.168.1.14> /root/broker && reset-mcu'
|
||||
```
|
||||
On the *BridgeProps* you can read the broker IP address from Linino filesystem with this code:
|
||||
```csharp
|
||||
void updateBrokerAdressFromFile(const char* broker_file, BridgeProps* props)
|
||||
{
|
||||
// broker IP address is stored in Linino file systems and updated with ssh command by Room 2.0
|
||||
IPAddress ip;
|
||||
|
||||
Process _process;
|
||||
_process.begin("cat");
|
||||
_process.addParameter(broker_file); // for ssh remotely set broker address
|
||||
_process.run(); // run the process and wait for its termination
|
||||
String b;
|
||||
while (_process.available() > 0) {
|
||||
char c = _process.read();
|
||||
b += c;
|
||||
}
|
||||
b.trim();
|
||||
|
||||
if (ip.fromString(b.c_str())) props->setBrokerIpAddress(ip);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 6. Examples
|
||||
The library comes with a number of example sketches. See **File > Examples > ArduinoProps** within the Arduino IDE application.
|
||||
|
||||
See [EXAMPLES.md](EXAMPLES.md) for an adaptation of the Blink example (https://www.arduino.cc/en/tutorial/blink) as a simple MQTT props:
|
||||
|
||||
1. **BlinkOnBridgeProps**: the Blink example on a Yun props with *ArduinoProps library*
|
||||
2. **BlinkOnEthernetProps**: the Blink example on an Ethernet props with *ArduinoProps library*
|
||||
3. **BlinkOnWifiProps**: the Blink example on a Wifi props with *ArduinoProps library*
|
||||
4. **BlinkOnBridgePubSub**: the Blink example on props using *PubSubClient* directly
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
**Marie FAURE** (Oct 18th, 2019)
|
||||
* company: FAURE SYSTEMS SAS
|
||||
* mail: *dev at faure dot systems*
|
||||
* github: <a href="https://github.com/fauresystems?tab=repositories" target="_blank">fauresystems</a>
|
||||
* web: <a href="https://www.live-escape.net/" target="_blank">Live Escape Grenoble</a>
|
||||
@@ -0,0 +1,57 @@
|
||||
# *PropsAction* class reference
|
||||
|
||||
*PropsAction* brings asynchronous-like behavior to sketch `loop()`.
|
||||
|
||||
|
||||
## Constructors
|
||||
* `PropsAction()`
|
||||
* `PropsAction(unsigned long interval, void (*function)())`
|
||||
|
||||
`function` is the callback executed at every `interval` milliseconds.
|
||||
|
||||
## Members
|
||||
* `void reset(unsigned long interval)`
|
||||
* `void disable()`
|
||||
* `void enable()`
|
||||
* `void check()`
|
||||
* `bool tick()`
|
||||
* `unsigned long getInterval()`
|
||||
|
||||
## Usage
|
||||
|
||||
### Create a callback
|
||||
```csharp
|
||||
void clignote()
|
||||
{
|
||||
if (clignoter.value()) {
|
||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||
led.setValue(digitalRead(LED_BUILTIN));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create a *PropsAction* instance
|
||||
```csharp
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote); // interval every 1000 milliseconds
|
||||
```
|
||||
|
||||
### Call the instance `check()` method ine the sketch `loop()`
|
||||
```csharp
|
||||
void loop()
|
||||
{
|
||||
...
|
||||
|
||||
clignoteAction.check(); // do your stuff, don't freeze the loop with delay() calls
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
**Marie FAURE** (Oct 18th, 2019)
|
||||
* company: FAURE SYSTEMS SAS
|
||||
* mail: *dev at faure dot systems*
|
||||
* github: <a href="https://github.com/fauresystems?tab=repositories" target="_blank">fauresystems</a>
|
||||
* web: <a href="https://www.live-escape.net/" target="_blank">Live Escape Grenoble</a>
|
||||
@@ -0,0 +1,76 @@
|
||||
# *PropsData* reference
|
||||
|
||||
*PropsData* tracks data changes and provides escape room 2.0 propocol `DATA` ready string format.
|
||||
|
||||
See **4. Application protocol for escape room 2.0 props** in [README.md](README.md).
|
||||
|
||||
*PropsData* is an interface (base class) implemented by 4 classes:
|
||||
* ***PropsDataDecimal***
|
||||
* ***PropsDataInteger***
|
||||
* ***PropsDataLogical***
|
||||
* ***PropsDataText***
|
||||
|
||||
## Constructors
|
||||
* `PropsDataDecimal(const char *, uint8_t digits = 2, double precision = 0.1, double initial = 0)`
|
||||
* `PropsDataInteger(const char *, double precision = 0.1, long initial = 0)`
|
||||
* `PropsDataLogical(const char *, const char *trueval = NULL, const char *falseval = NULL, bool initial = false)`
|
||||
* `PropsDataText(const char *)`
|
||||
|
||||
By default for *PropsDataLogical*, `trueval` is `"1"` band `falseval` is `"0"`.
|
||||
|
||||
For *PropsDataDecimal*, `digits` is the number of decimal digits in the display string.
|
||||
|
||||
For *PropsDataDecimal* and *PropsDataInteger*, `precision` is the percentage threshold for deciding whether a change has occurred or not.
|
||||
|
||||
## Interface
|
||||
* `String fetch()`
|
||||
* `String fetchChange()`
|
||||
|
||||
*fetch()* always returns the `"var=value "` string ready to be sent in a `DATA` protocol message and update the `_previous` value.
|
||||
|
||||
*fetchChange()* checks whether a change has occurred or not. If a change occured, returns the `"var=value "` string ready to be sent in a `DATA` protocol message and update the `_previous` value. If no change occured, returns and empty string.
|
||||
|
||||
## Accessors and Mutators
|
||||
* `void setValue(const double|long|bool|String)`
|
||||
* `double|long|bool|String value() const`
|
||||
|
||||
Accessors and Mutators are typed according to *PropsData* type.
|
||||
|
||||
## Usage
|
||||
|
||||
### Create a *PropsData* instance
|
||||
```csharp
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
```
|
||||
|
||||
### Add the *PropsData* instance to managed data of the *Props*
|
||||
```csharp
|
||||
void setup()
|
||||
{
|
||||
...
|
||||
|
||||
Props::addManagedLogicalData(&clignoter);
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Use the *PropsData* instance anywhere
|
||||
```csharp
|
||||
clignoter.setValue(true);
|
||||
|
||||
...
|
||||
|
||||
if (clignoter.value()) {
|
||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||
led.setValue(digitalRead(LED_BUILTIN));
|
||||
}
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
**Marie FAURE** (Oct 18th, 2019)
|
||||
* company: FAURE SYSTEMS SAS
|
||||
* mail: *dev at faure dot systems*
|
||||
* github: <a href="https://github.com/fauresystems?tab=repositories" target="_blank">fauresystems</a>
|
||||
* web: <a href="https://www.live-escape.net/" target="_blank">Live Escape Grenoble</a>
|
||||
@@ -0,0 +1,224 @@
|
||||
# ArduinoProps library
|
||||
1. Installation and usage
|
||||
2. Escape room 2.0 props with Arduino
|
||||
3. *ArduinoProps library* value for escape room 2.0
|
||||
4. Application protocol for escape room 2.0 props
|
||||
5. Compatible hardware
|
||||
6. Advantages of Arduino Yun and Dragino Yun shield
|
||||
7. Advantages of the *ArduinoProps library*
|
||||
8. Limitations due to *PubSubClient*
|
||||
9. Examples
|
||||
|
||||
|
||||
## 1. Installation and usage
|
||||
Download `ArduinoProps-master.zip` from GitHub and use **Sketch > Include Library > Add .ZIP Library...**
|
||||
|
||||

|
||||
|
||||
Add `#include "ArduinoProps.h"` on top of your sketch and start coding. Reading [EXAMPLES.md](EXAMPLES.md) will be helpful.
|
||||
|
||||
|ArduinoProps library documentation |
|
||||
|-----------------------------------------------|
|
||||
| [This README file](README.md) |
|
||||
| [Props class reference](Props.md) |
|
||||
| [PropsLite class reference](PropsLite.md) |
|
||||
| [PropsData class reference](PropsData.md) |
|
||||
| [PropsAction class reference](PropsAction.md) |
|
||||
| [ArduinoProps library examples](EXAMPLES.md) |
|
||||
|
||||
|
||||
## 2. Escape room 2.0 props with Arduino
|
||||
|
||||
The *ArduinoProps library* allows to build *escape room 2.0 props* easily with Arduino and particularly with Arduino Yun or Arduino Mega 2560 + Dragino Yun shield however it works with Ethernet and Wifi shields too.
|
||||
|
||||
The *ArduinoProps library* relies on *<a href="https://github.com/knolleary/pubsubclient" target="_blank">Nick O'Leary PubSubClient</a>* library for MQTT. MQTT is a lightweight publish/subscribe messaging protocol designed for M2M (machine to machine) telemetry in low bandwidth environments.
|
||||
|
||||
You will find Raspberry Pi libraries for escape room 2.0 props at https://github.com/fauresystems
|
||||
|
||||
|
||||
## 3. *ArduinoProps library* value for escape room 2.0
|
||||
*Nick O'Leary PubSubClient* library is sufficient to write escape room 2.0 props Arduino code.
|
||||
|
||||
The *ArduinoProps library* relies on *Nick O'Leary PubSubClient* for MQTT messaging and provides a framework and protocol for helping and accelerating the coding of Arduino props. If you will help build simple and complex props with reliable code.
|
||||
|
||||
Since 2016, *ArduinoProps library* is used extensively in many escape rooms with up to 20 props per room (which is no limit at all), with a high level of availability and sustainability.
|
||||
|
||||
### Inbox/oubox messaging
|
||||
Instead of usual MQTT topic structure which sends every data value in a different topic for every variable, *ArduinoProps library* sends data via a unique outbox topic.
|
||||
|
||||
Props receives commands in its inbox MQTT topic.
|
||||
|
||||
Props can subscribe to any other MQTT topic to receive othe information, for example the escape game scenario (English/French/Kids) or the game clock countdown.
|
||||
|
||||
### Escape room structured MQTT topics
|
||||
If you're running our Escape Room control software *Room 2.0* to supervise the escape room in real-time you have to respect its syntax for inbox/outbnox MQTT topics:
|
||||
```csharp
|
||||
Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
|
||||
example:
|
||||
Room/Demoniak/Props/Arduino Contrôleur/inbox
|
||||
Room/Demoniak/Props/Arduino Contrôleur/outbox
|
||||
```
|
||||
MQTT supports UTF-8 encoding.
|
||||
|
||||
### Sending messages only when appropriate
|
||||
Usually Arduino IoT sketches send MQTT data for every variable at every loop. When the loop is fast, you can get tons of useless messages, and usually a sleeping delay is added at every loop to slow down the message flow ; this can slow down significantly the props response time to human supervision and automation commands.
|
||||
|
||||
While Arduino sketch is sending a message it isn't doing props sensor/actuator processing, and it takes about 20 to 40 milliseconds to send a message. With too many messsages it's too much processing time wasted.
|
||||
|
||||
*ArduinoProps library* sends all data periodically every 30 seconds (default parameter) and sends data changes over a period of time you choose (typical 400 milliseconds for real-time behavior, but you may choose every 100 milliseconds, 1 or 3 seconds: your choice for your application).
|
||||
|
||||
Therefore the minimum processing time is used to send messages.
|
||||
|
||||
Tracking data changes could make Arduino code difficult to read and maintain but the. *ArduinoProps library* will hide this mechanism and make the code much more readable.
|
||||
|
||||
Some analog data can change at every loop but not significantly, for example U=2.77 volts while showing this when it changes more than 10% could be sufficient. *ArduinoProps library* offers a nice syntax to take care of this significance, with simple code.
|
||||
|
||||
### Maintaining MQTT server connection
|
||||
MQTT connection state must be checked at erevy sketch loop as well as incoming MQTT messages. *ArduinoProps library* does all in one code line.
|
||||
|
||||
And on Yun this same code line can switch MQTT server IP address (can be helpful in a fallback plan).
|
||||
|
||||
### Simple human-readable text protocol
|
||||
An escape room 2.0 is typically centrally controlled from a Windows PC which provides the Game Master with monitoring and control information as well as some automation.
|
||||
|
||||
To unifiy escape room 2.0 development, *ArduinoProps library* provides a simple protocol between props and room controller at application level:
|
||||
```csharp
|
||||
DATA -> send variables to control
|
||||
MESG -> send text to display in control
|
||||
DONE -> aknowledge a command has been performed
|
||||
OMIT -> aknowledge a command has been ignored
|
||||
OVER -> notify a challenge is over
|
||||
REQU -> request a command to another props
|
||||
PROG -> request a control program
|
||||
```
|
||||
|
||||
### Handling long message seamlessly and efficiently
|
||||
MQTT standard limitations are far above what we need:
|
||||
- maximum topic length 65,536 bytes (64k)
|
||||
- maximum message size 268,435,456 bytes (256M)
|
||||
|
||||
However, on Arduino the limit is for the addition of topic length + message length:
|
||||
- Wifi shield limit is 80 bytes
|
||||
- Yun limit (due to Bridge) is 120 bytes
|
||||
|
||||
DATA messages when many I/O like on Arduino Mega 2560 and MESG information messages can go very long, and crash the sketch without notice at run-time.
|
||||
|
||||
*ArduinoProps library* seamlessly splits long messages smartly when required.
|
||||
|
||||
## 4. Application protocol for escape room 2.0 props
|
||||
|
||||
A *level 7* protocol (*application level*) is necessary for connected props to report data and to be controlled for the escape room automation and game play.
|
||||
|
||||
We have defined a simple human-readable text protocol. Messages are encoded in UTF-8, in Arduino sketches use:
|
||||
```csharp
|
||||
str = u8"la chaîne avec des caractères non Latin1";
|
||||
```
|
||||
|
||||
Messages are sent/received thru outbox/inbox MQTT topics:
|
||||
```csharp
|
||||
Room/[escape room name]/Props/[propsname]/inbox
|
||||
Room/[escape room name]/Props/[propsname]/outbox
|
||||
```
|
||||
|
||||
Props connection state is handled in the outbox topic:
|
||||
* with a `DISCONNECTED` **Will** when MQTT server connection is broken
|
||||
* with a `CONNECTED` **retained message** when MQTT server connection is established.
|
||||
|
||||
More MQTT topics can be use for anything (room scenario, etc.).
|
||||
|
||||
#### Messages sent from the Arduino props are formatted
|
||||
```csharp
|
||||
DATA sensor=24.26 light=on challenge=pinball
|
||||
MESG Warning: sensor not detected
|
||||
DONE power:1
|
||||
OMIT power:1
|
||||
OVER pinball
|
||||
REQU Arduino Relay -> door:open
|
||||
PROG audio-final:start
|
||||
```
|
||||
|
||||
#### Message received by the Arduino props have no particular format
|
||||
```csharp
|
||||
power:1 for example to power on the room
|
||||
porte-salon:ouvrir for example to open a door
|
||||
lumière-salon:éteindre for example to switch-off a light
|
||||
```
|
||||
|
||||
|
||||
#### @PING-PONG special messages
|
||||
```csharp
|
||||
@PING is received in inbox then the props sends just PONG in outbox
|
||||
-> so the escape room controller can monitor response time at application level
|
||||
```
|
||||
|
||||
|
||||
## 5. Compatible hardware
|
||||
You can use the *ArduinoProps library* with any *Nick O'Leary PubSubClient* supported hardware. It uses the Arduino Ethernet Client api for interacting with the underlying network hardware. This means it Just Works with a growing number of boards and shields, including:
|
||||
|
||||
- Arduino Ethernet
|
||||
- Arduino Ethernet Shield
|
||||
- Arduino Yun – use the included `YunClient` in place of `EthernetClient`, and
|
||||
be sure to do a `Bridge.begin()` first
|
||||
- Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield,
|
||||
enable the `MQTT_MAX_TRANSFER_SIZE` define in `PubSubClient.h`.
|
||||
- Sparkfun WiFly Shield – [library](https://github.com/dpslwk/WiFly)
|
||||
- TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library)
|
||||
- Intel Galileo/Edison
|
||||
- ESP8266
|
||||
- ESP32
|
||||
|
||||
Please check at https://github.com/knolleary/pubsubclient
|
||||
|
||||
|
||||
## 6. Advantages of Arduino Yun and Dragino Yun shield
|
||||
In escape room 2.0, props are usually powered by Arduino Yun or Arduino Mega 2560 + Dragino Yun shield (or Raspberry Pi) because they are cheap and appropriate for DIY projects.
|
||||
|
||||
***The advantage of Yun versus Ethernet/Wifi shields is that you can upload sketches remotely to the micro-controller during the whole props lifetime*** (no need to open boxes and break walls to plug an USB cable when a sketch modification is required).
|
||||
|
||||
Arduino Mega 2560 + Dragino Yun shield offers much more memory and many more pins thann an Arduino Yun for same price.
|
||||
|
||||
|
||||
## 7. Advantages of the *ArduinoProps library*
|
||||
You can build escape room 2.0 Arduino-based props just with Nick O'Leary PubSubClient library for MQTT.
|
||||
|
||||
With *ArduinoProps library*:
|
||||
- create connected props quickly thanks to the application protocol and examples
|
||||
- targetted for escape room props, *ArduinoProps library* can be used for any IoT project
|
||||
- re-use your code when creating new escape rooms****
|
||||
- rely on *ArduinoProps library* robustness: for example, ***the library handles the Arduino Yun Bridge transfer size limitation by smartly splitting large messages (only) when necessary***.
|
||||
|
||||
**Note:** sending too large messages to Arduino Bridge breaks the Arduino sketch. *ArduinoProps library* prevents this crash case.
|
||||
|
||||
*ArduinoProps library* itself relies on *Nick O'Leary PubSubClient* library for MQTT.
|
||||
|
||||
|
||||
## 8. Limitations due to *PubSubClient*
|
||||
These limitations are not an issue for escape room props:
|
||||
- It can only publish QoS 0 messages.
|
||||
- It can subscribe at QoS 0 or QoS 1.
|
||||
- The keepalive interval is set to 15 seconds by default. This is configurable
|
||||
via `MQTT_KEEPALIVE` in `PubSubClient.h`.
|
||||
- The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
|
||||
changing value of `MQTT_VERSION` in `PubSubClient.h`.
|
||||
|
||||
|
||||
## 9. Examples
|
||||
|
||||
The library comes with a number of example sketches. See **File > Examples > ArduinoProps** within the Arduino IDE application.
|
||||
|
||||
See [EXAMPLES.md](EXAMPLES.md) for an adaptation of the Blink example (https://www.arduino.cc/en/tutorial/blink) as a simple MQTT props:
|
||||
|
||||
1. **BlinkOnBridgeProps**: the Blink example on a Yun props with *ArduinoProps library*
|
||||
2. **BlinkOnEthernetProps**: the Blink example on an Ethernet props with *ArduinoProps library*
|
||||
3. **BlinkOnWifiProps**: the Blink example on a Wifi props with *ArduinoProps library*
|
||||
4. **BlinkOnBridgePubSub**: the Blink example on props using *PubSubClient* directly
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
**Marie FAURE** (Oct 18th, 2019)
|
||||
* company: FAURE SYSTEMS SAS
|
||||
* mail: *dev at faure dot systems*
|
||||
* github: <a href="https://github.com/fauresystems?tab=repositories" target="_blank">fauresystems</a>
|
||||
* web: <a href="https://www.live-escape.net/" target="_blank">Live Escape Grenoble</a>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,109 @@
|
||||
/* BlinkOnBridgeProps.ino
|
||||
MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Adapt the Blink example (https://www.arduino.cc/en/tutorial/blink) as a
|
||||
simple MQTT props. Avoid delay() calls (except short ones) in loop() to
|
||||
ensure CPU for MQTT protocol. Use PropsAction checks instead.
|
||||
|
||||
Copy and change it to build your fist Arduino connected props, you will
|
||||
only be limited by your imagination.
|
||||
|
||||
Requirements: install ArduinoProps.zip library.
|
||||
*/
|
||||
#include <Bridge.h>
|
||||
#include "ArduinoProps.h"
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// prpos inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
|
||||
BridgeProps props(u8"Arduino Contrôleur", // as MQTT client id, should be unique per client for given broker
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/inbox",
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/outbox",
|
||||
"192.168.1.42", // your MQTT server IP address
|
||||
1883); // your MQTT server port;
|
||||
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
PropsDataLogical led(u8"led");
|
||||
|
||||
void clignote(); // forward
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Bridge.begin();
|
||||
//updateBrokerAdressFromFile("/root/broker", &props); // if you're running our Escape Room control software (Room 2.0)
|
||||
|
||||
props.addData(&clignoter);
|
||||
props.addData(&led);
|
||||
|
||||
props.begin(InboxMessage::run);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output
|
||||
|
||||
// At this point, the broker is not connected yet
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
props.loop();
|
||||
|
||||
led.setValue(digitalRead(LED_BUILTIN)); // read I/O
|
||||
|
||||
clignoteAction.check(); // do your stuff, don't freeze the loop with delay() calls
|
||||
}
|
||||
|
||||
void clignote()
|
||||
{
|
||||
if (clignoter.value()) {
|
||||
led.setValue(!led.value());
|
||||
digitalWrite(LED_BUILTIN, led.value() ? HIGH : LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void InboxMessage::run(String a) {
|
||||
|
||||
if (a == u8"app:startup")
|
||||
{
|
||||
props.sendAllData();
|
||||
props.sendDone(a);
|
||||
}
|
||||
else if (a == "clignoter:1")
|
||||
{
|
||||
clignoter.setValue(true);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else if (a == "clignoter:0")
|
||||
{
|
||||
clignoter.setValue(false);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge omition of the props command
|
||||
props.sendOmit(a);
|
||||
}
|
||||
}
|
||||
|
||||
void updateBrokerAdressFromFile(const char* broker_file, BridgeProps* props)
|
||||
{
|
||||
// broker IP address is stored in Linino file systems and updated with ssh command by Room 2.0
|
||||
IPAddress ip;
|
||||
|
||||
Process _process;
|
||||
_process.begin("cat");
|
||||
_process.addParameter(broker_file); // for ssh remotely set broker address
|
||||
_process.run(); // run the process and wait for its termination
|
||||
String b;
|
||||
while (_process.available() > 0) {
|
||||
char c = _process.read();
|
||||
b += c;
|
||||
}
|
||||
b.trim();
|
||||
|
||||
if (ip.fromString(b.c_str())) props->setBrokerIpAddress(ip);
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/* BlinkOnBridgePubSub.ino
|
||||
MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Adapt the Blink example (https://www.arduino.cc/en/tutorial/blink) as a
|
||||
simple MQTT props with PubSubClient.
|
||||
*/
|
||||
#include <Bridge.h>
|
||||
#include <BridgeClient.h>
|
||||
#include <Process.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <VariableTimedAction.h>
|
||||
|
||||
#define BROKER "192.168.1.42" // your MQTT server IP address
|
||||
#define PROPS_NAME u8"Arduino Contrôleur" // as MQTT client id, should be unique per client for given broker
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// props inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
#define PROPS_INBOX u8"Room/Demoniak/Props/Arduino Contrôleur/inbox"
|
||||
#define PROPS_OUTBOX u8"Room/Demoniak/Props/Arduino Contrôleur/outbox"
|
||||
|
||||
// Yun can store broker IP address in Linino file systems, and updatred with ssh command
|
||||
#define YUN_BROKER_FILE "/root/broker"
|
||||
|
||||
void publishAll(); // forward
|
||||
void publishChanges(); // forward
|
||||
|
||||
class Blinking : public VariableTimedAction {
|
||||
private:
|
||||
//this method will be called at your specified interval
|
||||
unsigned long run() {
|
||||
//swicth LED if blinking mode
|
||||
if (blink) {
|
||||
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||
}
|
||||
|
||||
//return code of 0 indicates no change to the interval
|
||||
//if the interval must be changed, then return the new interval
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
bool blink;
|
||||
};
|
||||
|
||||
class PublishAllData : public VariableTimedAction {
|
||||
private:
|
||||
unsigned long run() {
|
||||
publishAll();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class PublishChangedData : public VariableTimedAction {
|
||||
private:
|
||||
unsigned long run() {
|
||||
publishChanges();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
Blinking blinking;
|
||||
|
||||
bool led_ref;
|
||||
bool blink_ref;
|
||||
|
||||
BridgeClient _ethClient;
|
||||
PubSubClient _client(_ethClient);
|
||||
IPAddress _brokerAddress;
|
||||
|
||||
void callback(char*, byte*, unsigned int); // forward
|
||||
|
||||
// MQTT
|
||||
unsigned long lastReconnection(0L);
|
||||
PublishAllData publishAllData;
|
||||
PublishChangedData publishChangedData;
|
||||
|
||||
void setup()
|
||||
{
|
||||
Bridge.begin();
|
||||
|
||||
Process p;
|
||||
p.begin("cat");
|
||||
p.addParameter(YUN_BROKER_FILE);
|
||||
p.run(); // run the process and wait for its termination
|
||||
String b;
|
||||
while (p.available() > 0) {
|
||||
char c = p.read();
|
||||
b += c;
|
||||
}
|
||||
b.trim();
|
||||
|
||||
if (!_brokerAddress.fromString(b.c_str())) _brokerAddress.fromString(BROKER);
|
||||
_client.setServer(_brokerAddress, 1883);
|
||||
_client.setCallback(callback);
|
||||
|
||||
publishAllData.start(30000);
|
||||
publishChangedData.start(400);
|
||||
|
||||
blinking.blink = true;
|
||||
blinking.start(1000);
|
||||
blink_ref = false;
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
led_ref = LOW;
|
||||
|
||||
// At this point, the broker is not connected yet
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
if (_client.connected())
|
||||
{
|
||||
_client.loop();
|
||||
}
|
||||
else if (millis() > lastReconnection)
|
||||
{
|
||||
lastReconnection += 5000L;
|
||||
|
||||
if (_client.connect(PROPS_NAME, PROPS_OUTBOX, 2, true, "DISCONNECTED"))
|
||||
{
|
||||
_client.publish(PROPS_OUTBOX, "CONNECTED", true);
|
||||
_client.subscribe(PROPS_INBOX, 1); // max QoS is 1 for PubSubClient subsciption
|
||||
lastReconnection = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
// automation code should be here (nothing to do for this example)
|
||||
|
||||
VariableTimedAction::updateActions();
|
||||
}
|
||||
|
||||
void publishAll()
|
||||
{
|
||||
String buf = "DATA";
|
||||
|
||||
bool led = digitalRead(LED_BUILTIN);
|
||||
|
||||
buf += u8" led=" + (led ? String("1") : String("0"));
|
||||
led_ref = led;
|
||||
|
||||
buf += u8" clignote=" + (blinking.blink ? String("oui") : String("non"));
|
||||
blink_ref = blinking.blink;
|
||||
|
||||
_client.publish(PROPS_OUTBOX, buf.c_str());
|
||||
}
|
||||
|
||||
void publishChanges()
|
||||
{
|
||||
String buf = "DATA";
|
||||
|
||||
bool led = digitalRead(LED_BUILTIN);
|
||||
|
||||
if (led != led_ref)
|
||||
{
|
||||
buf += u8" led=" + (led ? String("1") : String("0"));
|
||||
led_ref = led;
|
||||
}
|
||||
|
||||
if (blinking.blink != blink_ref)
|
||||
{
|
||||
buf += u8" clignote=" + (blinking.blink ? String("oui") : String("non"));
|
||||
blink_ref = blinking.blink;
|
||||
}
|
||||
|
||||
if (buf.length() > 4)
|
||||
_client.publish(PROPS_OUTBOX, buf.c_str());
|
||||
}
|
||||
|
||||
void publishDone(String a)
|
||||
{
|
||||
a = "DONE " + a;
|
||||
_client.publish(PROPS_OUTBOX, a.c_str());
|
||||
}
|
||||
|
||||
void onInboxMessage(String a) {
|
||||
|
||||
if (a == u8"app:startup")
|
||||
{
|
||||
publishAll();
|
||||
publishDone(a);
|
||||
}
|
||||
else if (a == "clignoter:1")
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
blinking.blink = true;
|
||||
|
||||
publishAll(); // all data change, we don't have to be selctive then
|
||||
publishDone(a); // acknowledge props command action
|
||||
}
|
||||
else if (a == "clignoter:0")
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
blinking.blink = false;
|
||||
|
||||
publishAll(); // all data change, we don't have to be selctive then
|
||||
publishDone(a); // acknowledge props command action
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge omition of the props command
|
||||
_client.publish(PROPS_OUTBOX, String("OMIT " + a).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int len)
|
||||
{
|
||||
if (len)
|
||||
{
|
||||
char* p = (char*)malloc(len + 1);
|
||||
memcpy(p, payload, len);
|
||||
p[len] = '\0';
|
||||
if (String(p) == "@PING")
|
||||
_client.publish(PROPS_OUTBOX, "PONG");
|
||||
else
|
||||
onInboxMessage(p);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/* BlinkOnEthernetProps.ino
|
||||
MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Adapt the Blink example (https://www.arduino.cc/en/tutorial/blink) as a
|
||||
simple MQTT props. Avoid delay() calls (except short ones) in loop() to
|
||||
ensure CPU for MQTT protocol. Use PropsAction checks instead.
|
||||
|
||||
Copy and change it to build your fist Arduino connected props, you will
|
||||
only be limited by your imagination.
|
||||
|
||||
Requirements: install ArduinoProps.zip library.
|
||||
*/
|
||||
#include <Ethernet.h>
|
||||
#include <IPAddress.h>
|
||||
#include "ArduinoProps.h"
|
||||
|
||||
byte mac[] = { 0xFC, 0xFA, 0xEA, 0x00, 0x00, 0x02 }; //<<< MAKE SURE IT'S UNIQUE IN YOUR NETWORK!!!
|
||||
String ip = "192.168.1.19"; //<<< ENTER YOUR IP ADDRESS HERE ("" for DHCP)
|
||||
|
||||
// Builtin led is not available with the shield
|
||||
#undef LED_BUILTIN
|
||||
#define LED_BUILTIN 8
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// prpos inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
|
||||
EthernetProps props(u8"Arduino Contrôleur", // as MQTT client id, should be unique per client for given broker
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/inbox",
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/outbox",
|
||||
"192.168.1.42", // your MQTT server IP address
|
||||
1883); // your MQTT server port;
|
||||
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
PropsDataLogical led(u8"led");
|
||||
|
||||
void clignote(); // forward
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// can do more static IP configuration
|
||||
//Ethernet.setSubnetMask(IPAddress());
|
||||
//Ethernet.setGatewayIP(IPAddress());
|
||||
//Ethernet.setDnsServerIP(IPAddress());
|
||||
|
||||
IPAddress ipa;
|
||||
if (!ipa.fromString(ip)) {
|
||||
// attempt a DHCP connection:
|
||||
if (!Ethernet.begin(mac)) {
|
||||
// if DHCP fails, start with a hard-coded address:
|
||||
Ethernet.begin(mac, IPAddress(10, 90, 90, 239));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Ethernet.begin(mac, ipa);
|
||||
}
|
||||
delay(1500); // time for shield stuff
|
||||
|
||||
props.addData(&clignoter);
|
||||
props.addData(&led);
|
||||
|
||||
props.begin(InboxMessage::run);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output
|
||||
|
||||
// At this point, the broker is not connected yet
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
props.loop();
|
||||
|
||||
led.setValue(digitalRead(LED_BUILTIN)); // read I/O
|
||||
|
||||
clignoteAction.check(); // do your stuff, don't freeze the loop with delay() calls
|
||||
}
|
||||
|
||||
void clignote()
|
||||
{
|
||||
if (clignoter.value()) {
|
||||
led.setValue(!led.value());
|
||||
digitalWrite(LED_BUILTIN, led.value() ? HIGH : LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void InboxMessage::run(String a) {
|
||||
|
||||
if (a == u8"app:startup")
|
||||
{
|
||||
props.sendAllData();
|
||||
props.sendDone(a);
|
||||
}
|
||||
else if (a == "clignoter:1")
|
||||
{
|
||||
clignoter.setValue(true);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else if (a == "clignoter:0")
|
||||
{
|
||||
clignoter.setValue(false);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge omition of the props command
|
||||
props.sendOmit(a);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/* BlinkOnWifiProps.ino
|
||||
MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Adapt the Blink example (https://www.arduino.cc/en/tutorial/blink) as a
|
||||
simple MQTT props. Avoid delay() calls (except short ones) in loop() to
|
||||
ensure CPU for MQTT protocol. Use PropsAction checks instead.
|
||||
|
||||
Copy and change it to build your fist Arduino connected props, you will
|
||||
only be limited by your imagination.
|
||||
|
||||
Requirements: install ArduinoProps.zip library.
|
||||
*/
|
||||
#include "ArduinoProps.h"
|
||||
|
||||
// Setup your WiFi network
|
||||
const char* ssid = "";
|
||||
const char *passphrase = "";
|
||||
|
||||
// Builtin led is not available with the shield
|
||||
#undef LED_BUILTIN
|
||||
#define LED_BUILTIN 8
|
||||
|
||||
// If you're running our Escape Room control software (Room 2.0) you have to respect
|
||||
// prpos inbox/outbox syntax Room/[escape room name]/Props/[propsname]/inbox|outbox
|
||||
// https://live-escape.net/go/room
|
||||
|
||||
WifiProps props(u8"Arduino Contrôleur", // as MQTT client id, should be unique per client for given broker
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/inbox",
|
||||
u8"Room/Demoniak/Props/Arduino Contrôleur/outbox",
|
||||
"192.168.1.42", // your MQTT server IP address
|
||||
1883); // your MQTT server port;
|
||||
|
||||
PropsDataLogical clignoter(u8"clignote", u8"oui", u8"non", true);
|
||||
PropsDataLogical led(u8"led");
|
||||
|
||||
void clignote(); // forward
|
||||
PropsAction clignoteAction = PropsAction(1000, clignote);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// can do more static IP configuration
|
||||
//WiFi.config(IPAddress(), IPAddress(), IPAddress());
|
||||
//WiFi.config(IPAddress(), IPAddress());
|
||||
//WiFi.config(IPAddress());
|
||||
|
||||
WiFi.begin(ssid, passphrase);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
}
|
||||
|
||||
props.addData(&clignoter);
|
||||
props.addData(&led);
|
||||
|
||||
props.begin(InboxMessage::run);
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin LED_BUILTIN as an output
|
||||
|
||||
// At this point, the broker is not connected yet
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
props.loop();
|
||||
|
||||
led.setValue(digitalRead(LED_BUILTIN)); // read I/O
|
||||
|
||||
clignoteAction.check(); // do your stuff, don't freeze the loop with delay() calls
|
||||
}
|
||||
|
||||
void clignote()
|
||||
{
|
||||
if (clignoter.value()) {
|
||||
led.setValue(!led.value());
|
||||
digitalWrite(LED_BUILTIN, led.value() ? HIGH : LOW);
|
||||
}
|
||||
}
|
||||
|
||||
void InboxMessage::run(String a) {
|
||||
|
||||
if (a == u8"app:startup")
|
||||
{
|
||||
props.sendAllData();
|
||||
props.sendDone(a);
|
||||
}
|
||||
else if (a == "clignoter:1")
|
||||
{
|
||||
clignoter.setValue(true);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else if (a == "clignoter:0")
|
||||
{
|
||||
clignoter.setValue(false);
|
||||
|
||||
props.sendAllData(); // all data change, we don't have to be selctive then
|
||||
props.sendDone(a); // acknowledge props command action
|
||||
}
|
||||
else
|
||||
{
|
||||
// acknowledge omition of the props command
|
||||
props.sendOmit(a);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,59 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For PubSubClient
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Props KEYWORD1
|
||||
BridgeProps KEYWORD1
|
||||
EthernetProps KEYWORD1
|
||||
WifiProps KEYWORD1
|
||||
PropsAction KEYWORD1
|
||||
PropsData KEYWORD1
|
||||
PropsDataDecimal KEYWORD1
|
||||
PropsDataInteger KEYWORD1
|
||||
PropsDataLogical KEYWORD1
|
||||
PropsDataText KEYWORD1
|
||||
PropsCallback KEYWORD1
|
||||
PropsMessageReceived KEYWORD1
|
||||
InboxMessage KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
addData KEYWORD2
|
||||
loop KEYWORD2
|
||||
run KEYWORD2
|
||||
sendAllData KEYWORD2
|
||||
sendDataChanges KEYWORD2
|
||||
sendData KEYWORD2
|
||||
sendDone KEYWORD2
|
||||
sendMesg KEYWORD2
|
||||
sendOmit KEYWORD2
|
||||
sendOver KEYWORD2
|
||||
sendProg KEYWORD2
|
||||
sendRequ KEYWORD2
|
||||
reset KEYWORD2
|
||||
disable KEYWORD2
|
||||
enable KEYWORD2
|
||||
check KEYWORD2
|
||||
tick KEYWORD2
|
||||
getInterval KEYWORD2
|
||||
fetch KEYWORD2
|
||||
fetchChange KEYWORD2
|
||||
setValue KEYWORD2
|
||||
value KEYWORD2
|
||||
onInboxMessageReceived KEYWORD2
|
||||
setBrokerIpAddress KEYWORD2
|
||||
checkDataChanges KEYWORD2
|
||||
resetIntervals KEYWORD2
|
||||
send KEYWORD2
|
||||
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,9 @@
|
||||
name=ArduinoProps
|
||||
version=1.0.0
|
||||
author=Marie Faure
|
||||
maintainer=Marie Faure <dev at faure dot systems>
|
||||
sentence=ArduinoProps Library
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=https://github.com/fauresystems
|
||||
architectures=*
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
Name: ArduinoProps.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Library to make props sketch for Escape Room 2.0 (connected props).
|
||||
*/
|
||||
|
||||
#include "ArduinoProps.h"
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Name: ArduinoProps.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Library to make props sketch for Escape Room 2.0 (connected props).
|
||||
*/
|
||||
|
||||
#ifndef _ArduinoProps_h
|
||||
#define _ArduinoProps_h
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include "BridgeProps.h"
|
||||
#include "EthernetProps.h"
|
||||
#include "WifiProps.h"
|
||||
#include "PropsAction.h"
|
||||
#include "PropsData.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Name: BridgeProps.cpp
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Class Props for Arduino Yun and Dragino Yun shield.
|
||||
*/
|
||||
|
||||
#include "BridgeProps.h"
|
||||
|
||||
BridgeProps::BridgeProps(const char* client_id, const char* in_box, const char* out_box, const char* broker, const int port)
|
||||
: Props(client_id, in_box, out_box, broker, port)
|
||||
{
|
||||
_client.setClient(_bridgeClient);
|
||||
}
|
||||
|
||||
void BridgeProps::begin(void(*on_message)(String))
|
||||
{
|
||||
if (on_message) onInboxMessageReceived = on_message;
|
||||
}
|
||||
|
||||
void BridgeProps::setBrokerIpAddress(IPAddress ip)
|
||||
{
|
||||
_brokerIpAddress = ip;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Name: BridgeProps.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Class Props for Arduino Yun and Dragino Yun shield.
|
||||
*/
|
||||
#ifndef BRIDGEPROPS_H
|
||||
#define BRIDGEPROPS_H
|
||||
|
||||
#include <BridgeClient.h>
|
||||
#include <IPAddress.h>
|
||||
#include "Props.h"
|
||||
|
||||
class BridgeProps : public Props
|
||||
{
|
||||
public:
|
||||
BridgeProps(const char*, const char*, const char*, const char*, const int);
|
||||
void begin(void(*)(String) = NULL);
|
||||
void setBrokerIpAddress(IPAddress);
|
||||
|
||||
private:
|
||||
BridgeClient _bridgeClient;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Name: EthernetProps.cpp
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Class Props for Arduino with Ethernet shield.
|
||||
*/
|
||||
|
||||
#include "EthernetProps.h"
|
||||
|
||||
EthernetProps::EthernetProps(const char* client_id, const char* in_box, const char* out_box, const char* broker, const int port)
|
||||
: Props(client_id, in_box, out_box, broker, port)
|
||||
{
|
||||
_client.setClient(_ethernetClient);
|
||||
}
|
||||
|
||||
void EthernetProps::begin(void(*on_message)(String))
|
||||
{
|
||||
if (on_message) onInboxMessageReceived = on_message;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Name: EthernetProps.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Class Props for Arduino with Ethernet shield.
|
||||
*/
|
||||
#ifndef ETHERNETPROPS_H
|
||||
#define ETHERNETPROPS_H
|
||||
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
#include <EthernetClient.h>
|
||||
#include "Props.h"
|
||||
|
||||
class EthernetProps : public Props
|
||||
{
|
||||
public:
|
||||
EthernetProps(const char*, const char*, const char*, const char*, const int);
|
||||
void begin(void(*)(String) = NULL);
|
||||
|
||||
private:
|
||||
EthernetClient _ethernetClient;
|
||||
};
|
||||
|
||||
#endif
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
Name: Props.cpp
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Base class to make props sketch for Escape Room 2.0 (connected).
|
||||
*/
|
||||
|
||||
#include "Props.h"
|
||||
#include <Process.h>
|
||||
|
||||
void PropsCallback::run(char* topic, byte* payload, unsigned int len)
|
||||
{
|
||||
if (len)
|
||||
{
|
||||
char* p = (char*)malloc(len + 1);
|
||||
memcpy(p, payload, len);
|
||||
p[len] = '\0';
|
||||
if (String(p) == "@PING")
|
||||
client->publish(outbox, "PONG");
|
||||
else
|
||||
Props::onInboxMessageReceived(p);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
void PropsMessageReceived::run(String a)
|
||||
{
|
||||
client->publish(outbox, String("OMIT " + a).c_str());
|
||||
}
|
||||
|
||||
Props::Props(const char* client_id, const char* in_box, const char* out_box, const char* broker, const int port)
|
||||
{
|
||||
clientId = client_id;
|
||||
inbox = in_box;
|
||||
outbox = out_box;
|
||||
|
||||
_nextReconAttempt = 0;
|
||||
_dataSentCount = 0;
|
||||
_maximumSilentPeriod = 30; // 30 seconds
|
||||
_payloadMax = MQTT_MAX_PACKET_SIZE - (1 + 2 + strlen(Props::outbox));
|
||||
|
||||
_brokerIpAddress.fromString(broker);
|
||||
_client.setServer(_brokerIpAddress, port);
|
||||
|
||||
PropsCallback::client = &_client;
|
||||
PropsCallback::outbox = outbox;
|
||||
_client.setCallback(PropsCallback::run);
|
||||
|
||||
PropsMessageReceived::client = &_client;
|
||||
PropsMessageReceived::outbox = outbox;
|
||||
|
||||
_sendDataAction.reset(400); // check data changes every 400 milliseconds
|
||||
}
|
||||
|
||||
void Props::addData(PropsData* d)
|
||||
{
|
||||
_dataTable.push_back(d);
|
||||
}
|
||||
|
||||
void Props::loop()
|
||||
{
|
||||
if (_client.connected())
|
||||
{
|
||||
_client.loop();
|
||||
}
|
||||
else if (millis() > _nextReconAttempt)
|
||||
{
|
||||
_nextReconAttempt += 5000L;
|
||||
|
||||
if (_client.connect(clientId, outbox, 2, true, "DISCONNECTED"))
|
||||
{
|
||||
_client.publish(outbox, "CONNECTED", true);
|
||||
_client.subscribe(inbox, 1); // max QoS is 1 for PubSubClient subsciption
|
||||
_nextReconAttempt = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
if (_sendDataAction.tick()) checkDataChanges(); // send data changes if any
|
||||
}
|
||||
|
||||
void Props::checkDataChanges()
|
||||
{
|
||||
if (_dataSentCount) {
|
||||
sendDataChanges();
|
||||
} else {
|
||||
sendAllData();
|
||||
}
|
||||
|
||||
++_dataSentCount;
|
||||
|
||||
// don't be silent to long
|
||||
if (_dataSentCount > (_maximumSilentPeriod * 1000 / _sendDataAction.getInterval())) _dataSentCount = 0;
|
||||
}
|
||||
|
||||
void Props::sendAllData()
|
||||
{
|
||||
String data("DATA "), str;
|
||||
for (SimpleList<PropsData*>::iterator itr = _dataTable.begin(); itr != _dataTable.end(); ++itr) {
|
||||
str = (*itr)->fetch();
|
||||
send(&data, &str);
|
||||
}
|
||||
|
||||
if (data.length() > 5) {
|
||||
_client.publish(outbox, data.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Props::sendDataChanges()
|
||||
{
|
||||
String data("DATA "), str;
|
||||
for (SimpleList<PropsData*>::iterator itr = _dataTable.begin(); itr != _dataTable.end(); ++itr) {
|
||||
str = (*itr)->fetchChange();
|
||||
send(&data, &str);
|
||||
}
|
||||
|
||||
if (data.length() > 5) {
|
||||
_client.publish(outbox, data.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Props::send(String* d, String* s)
|
||||
{
|
||||
if (d->length() + s->length() <= _payloadMax) {
|
||||
*d += *s;
|
||||
} else if (s->length() > _payloadMax + 5) {
|
||||
sendMesg(u8"Data ignored, loo larged to be sent");
|
||||
} else if (d->length() + s->length() > _payloadMax) {
|
||||
_client.publish(outbox, d->c_str());
|
||||
*d = "DATA " + *s;
|
||||
}
|
||||
}
|
||||
|
||||
void Props::sendData(String data) {
|
||||
if (data.length() > 5) {
|
||||
_client.publish(outbox, String("DATA " + data).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Props::sendDone(String action) {
|
||||
_client.publish(outbox, String("DONE " + action).c_str());
|
||||
}
|
||||
|
||||
void Props::sendMesg(String mesg) {
|
||||
if (mesg.length() > 5) {
|
||||
_client.publish(outbox, String("MESG " + mesg).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Props::sendMesg(String mesg, char *topic) {
|
||||
if (mesg.length() > 5) {
|
||||
_client.publish(topic, String("MESG " + mesg).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Props::sendOmit(String action) {
|
||||
_client.publish(outbox, String("OMIT " + action).c_str());
|
||||
}
|
||||
|
||||
void Props::sendOver(String challenge) {
|
||||
_client.publish(outbox, String("OMIT " + challenge).c_str());
|
||||
}
|
||||
|
||||
void Props::sendProg(String program) {
|
||||
_client.publish(outbox, String("PROGRAM " + program).c_str());
|
||||
}
|
||||
|
||||
void Props::sendRequ(String request) {
|
||||
_client.publish(outbox, String("REQU " + request).c_str());
|
||||
}
|
||||
|
||||
void Props::sendRequ(String action, char* topic) {
|
||||
_client.publish(topic, String("OMIT " + action).c_str());
|
||||
}
|
||||
|
||||
void Props::resetIntervals(const int changes, const int silent)
|
||||
{
|
||||
_nextReconAttempt = 0;
|
||||
_dataSentCount = 0;
|
||||
_maximumSilentPeriod = silent; // seconds
|
||||
_sendDataAction.reset(changes); // milliseconds
|
||||
}
|
||||
|
||||
|
||||
PubSubClient *PropsCallback::client;
|
||||
const char *PropsCallback::outbox;
|
||||
PubSubClient *PropsMessageReceived::client;
|
||||
const char *PropsMessageReceived::outbox;
|
||||
void (*Props::onInboxMessageReceived)(String) = PropsMessageReceived::run;
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Name: Props.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Base class to make props sketch for Escape Room 2.0 (connected).
|
||||
*/
|
||||
#ifndef PROPS_H
|
||||
#define PROPS_H
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <BridgeClient.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <SimpleList.h>
|
||||
#include "PropsAction.h"
|
||||
#include "PropsData.h"
|
||||
|
||||
#ifndef MQTT_MAX_PACKET_SIZE
|
||||
#define MQTT_MAX_PACKET_SIZE 128 // Bridge tranfer stuff (see PubSubClient)
|
||||
#endif
|
||||
|
||||
class PropsCallback
|
||||
{
|
||||
public:
|
||||
static PubSubClient *client;
|
||||
static const char *outbox;
|
||||
static void run(char* topic, byte* payload, unsigned int len);
|
||||
};
|
||||
|
||||
class PropsMessageReceived
|
||||
{
|
||||
public:
|
||||
static PubSubClient *client;
|
||||
static const char *outbox;
|
||||
static void run(String);
|
||||
};
|
||||
|
||||
class InboxMessage : public PropsMessageReceived
|
||||
{
|
||||
public:
|
||||
static void run(String); // to be implemented in .ino sketch
|
||||
};
|
||||
|
||||
class Props
|
||||
{
|
||||
public:
|
||||
Props(const char*, const char*, const char*, const char*, const int=1883);
|
||||
virtual void begin(void(*)(String) = NULL) = 0;
|
||||
void addData(PropsData*);
|
||||
void loop();
|
||||
void resetIntervals(const int changes, const int silent);
|
||||
void sendAllData();
|
||||
void sendDataChanges();
|
||||
void sendData(String); // only in outbox
|
||||
void sendDone(String);
|
||||
void sendMesg(String);
|
||||
void sendMesg(String, char*);
|
||||
void sendOmit(String);
|
||||
void sendOver(String);
|
||||
void sendProg(String);
|
||||
void sendRequ(String);
|
||||
void sendRequ(String, char*);
|
||||
|
||||
static void (*onInboxMessageReceived)(String);
|
||||
|
||||
const char *clientId;
|
||||
const char *inbox;
|
||||
const char *outbox;
|
||||
|
||||
protected:
|
||||
void checkDataChanges();
|
||||
void send(String*, String*);
|
||||
|
||||
IPAddress _brokerIpAddress;
|
||||
PubSubClient _client;
|
||||
|
||||
uint16_t _dataSentCount;
|
||||
uint8_t _maximumSilentPeriod;
|
||||
unsigned long _nextReconAttempt;
|
||||
uint8_t _payloadMax;
|
||||
PropsAction _sendDataAction;
|
||||
SimpleList<PropsData*> _dataTable;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Name: PropsAction.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Provide an easy way of triggering functions at a set _interval.
|
||||
*/
|
||||
|
||||
#include "PropsAction.h"
|
||||
|
||||
PropsAction::PropsAction()
|
||||
{
|
||||
_active = true;
|
||||
_previous = 0;
|
||||
_interval = 400;
|
||||
_execute = NULL;
|
||||
}
|
||||
|
||||
PropsAction::PropsAction(unsigned long intervl, void (*function)())
|
||||
{
|
||||
_active = true;
|
||||
_previous = 0;
|
||||
_interval = intervl;
|
||||
_execute = function;
|
||||
}
|
||||
|
||||
void PropsAction::reset(unsigned long intervl)
|
||||
{
|
||||
_active = true;
|
||||
_previous = 0;
|
||||
_interval = intervl;
|
||||
}
|
||||
|
||||
void PropsAction::disable()
|
||||
{
|
||||
_active = false;
|
||||
}
|
||||
|
||||
void PropsAction::enable()
|
||||
{
|
||||
_active = true;
|
||||
}
|
||||
|
||||
void PropsAction::check() {
|
||||
if (_active && (millis() - _previous >= _interval))
|
||||
{
|
||||
_previous = millis();
|
||||
if (_execute) _execute();
|
||||
}
|
||||
}
|
||||
|
||||
bool PropsAction::tick() {
|
||||
if (_active && (millis() - _previous >= _interval))
|
||||
{
|
||||
_previous = millis();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long PropsAction::getInterval()
|
||||
{
|
||||
return _interval;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Name: PropsAction.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Provide an easy way of triggering functions at a set interval.
|
||||
*/
|
||||
|
||||
#ifndef PROPSACTION_H
|
||||
#define PROPSACTION_H
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
class Props;
|
||||
|
||||
class PropsAction {
|
||||
|
||||
public:
|
||||
PropsAction();
|
||||
PropsAction(unsigned long interval, void (*function)());
|
||||
|
||||
void reset(unsigned long interval);
|
||||
void disable();
|
||||
void enable();
|
||||
void check();
|
||||
bool tick();
|
||||
|
||||
unsigned long getInterval();
|
||||
|
||||
private:
|
||||
bool _active;
|
||||
unsigned long _previous;
|
||||
unsigned long _interval;
|
||||
void (*_execute)();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Name: PropsData.cpp
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Variable wrapper for connected props.
|
||||
*/
|
||||
|
||||
#include "PropsData.h"
|
||||
#include <String.h>
|
||||
|
||||
// ---- Decimal
|
||||
PropsDataDecimal::PropsDataDecimal(const char * id, uint8_t digits, double precision, double initial) {
|
||||
_id = id;
|
||||
_current = initial;
|
||||
_previous = _current;
|
||||
_precision = precision;
|
||||
_digits = digits;
|
||||
}
|
||||
|
||||
String PropsDataDecimal::fetch() {
|
||||
_previous = _current;
|
||||
return String(_id) + "=" + String(_current, _digits) + " ";
|
||||
}
|
||||
|
||||
String PropsDataDecimal::fetchChange() {
|
||||
if (_current != _previous && _current && abs(_current - _previous) / _current >= _precision) {
|
||||
return fetch();
|
||||
}
|
||||
else if (_current != _previous && !_current && abs(_current - _previous) >= _precision) {
|
||||
return fetch();
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void PropsDataDecimal::setValue(const double v) {
|
||||
_current = v;
|
||||
}
|
||||
|
||||
double PropsDataDecimal::value() const {
|
||||
return _current;
|
||||
}
|
||||
|
||||
// ---- Integer
|
||||
PropsDataInteger::PropsDataInteger(const char * id, double precision, long initial) {
|
||||
_id = id;
|
||||
_current = initial;
|
||||
_previous = _current;
|
||||
_precision = precision;
|
||||
}
|
||||
|
||||
String PropsDataInteger::fetch() {
|
||||
_previous = _current;
|
||||
return String(_id) + "=" + String(_current) + " ";
|
||||
}
|
||||
|
||||
String PropsDataInteger::fetchChange() {
|
||||
if (_current != _previous && _current && abs(_current - _previous) / _current >= _precision) {
|
||||
return fetch();
|
||||
}
|
||||
else if (_current != _previous && !_current && abs(_current - _previous) >= _precision) {
|
||||
return fetch();
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void PropsDataInteger::setValue(const long v) {
|
||||
_current = v;
|
||||
}
|
||||
|
||||
long PropsDataInteger::value() const {
|
||||
return _current;
|
||||
}
|
||||
|
||||
// ---- Logical
|
||||
PropsDataLogical::PropsDataLogical(const char * id, const char *trueval, const char *falseval, bool initial) {
|
||||
_id = id;
|
||||
_current = initial;
|
||||
_previous = _current;
|
||||
_trueString = trueval;
|
||||
_falseString = falseval;
|
||||
}
|
||||
|
||||
String PropsDataLogical::fetch() {
|
||||
String str(String(_id) + "=");
|
||||
if (_current) {
|
||||
str += _trueString ? _trueString : "1";
|
||||
} else {
|
||||
str += _falseString ? _falseString : "0";
|
||||
}
|
||||
str += " ";
|
||||
_previous = _current;
|
||||
return str;
|
||||
}
|
||||
|
||||
String PropsDataLogical::fetchChange() {
|
||||
if (_current != _previous) {
|
||||
return fetch();
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void PropsDataLogical::setValue(const bool v) {
|
||||
_current = v;
|
||||
}
|
||||
|
||||
bool PropsDataLogical::value() const {
|
||||
return _current;
|
||||
}
|
||||
|
||||
// ---- Text
|
||||
PropsDataText::PropsDataText(const char * id) {
|
||||
_id = id;
|
||||
}
|
||||
|
||||
String PropsDataText::fetch() {
|
||||
_previous = _current;
|
||||
return String(_id) + "=" + _current + " ";
|
||||
}
|
||||
|
||||
String PropsDataText::fetchChange() {
|
||||
if (_current != _previous) {
|
||||
return fetch();
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void PropsDataText::setValue(String v) {
|
||||
_current = v;
|
||||
}
|
||||
|
||||
String PropsDataText::value() const {
|
||||
return _current;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Name: PropsData.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Variable wrapper for connected props.
|
||||
*/
|
||||
#ifndef PROPSDATA_H
|
||||
#define PROPSDATA_H
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <WString.h>
|
||||
|
||||
class PropsData
|
||||
{
|
||||
public:
|
||||
virtual String fetch() = 0;
|
||||
virtual String fetchChange() = 0;
|
||||
};
|
||||
|
||||
class PropsDataDecimal : public PropsData
|
||||
{
|
||||
public:
|
||||
PropsDataDecimal(const char *, uint8_t digits = 2, double precision = 0.1, double initial = 0);
|
||||
String fetch();
|
||||
String fetchChange();
|
||||
void setValue(const double);
|
||||
double value() const;
|
||||
private:
|
||||
const char *_id;
|
||||
double _current;
|
||||
double _previous;
|
||||
double _precision;
|
||||
uint8_t _digits;
|
||||
};
|
||||
|
||||
class PropsDataInteger : public PropsData
|
||||
{
|
||||
public:
|
||||
PropsDataInteger(const char *, double precision = 0.1, long initial = 0);
|
||||
String fetch();
|
||||
String fetchChange();
|
||||
void setValue(const long);
|
||||
long value() const;
|
||||
private:
|
||||
const char *_id;
|
||||
long _current;
|
||||
long _previous;
|
||||
double _precision;
|
||||
};
|
||||
|
||||
class PropsDataLogical : public PropsData
|
||||
{
|
||||
public:
|
||||
PropsDataLogical(const char *, const char *trueval = NULL, const char *falseval = NULL, bool initial = false);
|
||||
String fetch();
|
||||
String fetchChange();
|
||||
void setValue(const bool);
|
||||
bool value() const;
|
||||
private:
|
||||
const char *_id;
|
||||
bool _current;
|
||||
bool _previous;
|
||||
const char *_trueString;
|
||||
const char *_falseString;
|
||||
};
|
||||
|
||||
class PropsDataText : public PropsData
|
||||
{
|
||||
public:
|
||||
PropsDataText(const char *);
|
||||
String fetch();
|
||||
String fetchChange();
|
||||
void setValue(String);
|
||||
String value() const;
|
||||
private:
|
||||
const char *_id;
|
||||
String _current;
|
||||
String _previous;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Name: WifiProps.cpp
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Class Props for Arduino with Wifi shield.
|
||||
*/
|
||||
|
||||
#include "WifiProps.h"
|
||||
|
||||
|
||||
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRVIDOR4000)
|
||||
|
||||
|
||||
WifiProps::WifiProps(const char* client_id, const char* in_box, const char* out_box, const char* broker, const int port)
|
||||
: Props(client_id, in_box, out_box, broker, port)
|
||||
{
|
||||
_client.setClient(_wifiClient);
|
||||
}
|
||||
|
||||
void WifiProps::begin(void(*on_message)(String))
|
||||
{
|
||||
if (on_message) onInboxMessageReceived = on_message;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Name: WifiProps.h
|
||||
Created: 29/10/2019 09:20:31
|
||||
Author: Marie Faure <dev at faure dot systems>
|
||||
Editor: https://github.com/fauresystems
|
||||
License: MIT License (c) Marie Faure <dev at faure dot systems>
|
||||
|
||||
Class Props for Arduino with Wifi shield.
|
||||
*/
|
||||
#ifndef WIFIPROPS_H
|
||||
#define WIFIPROPS_H
|
||||
|
||||
|
||||
#if defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_SAMD_MKRVIDOR4000)
|
||||
|
||||
|
||||
#include <WiFiNINA.h>
|
||||
|
||||
#include <SPI.h>
|
||||
#include <WiFiClient.h>
|
||||
#include "Props.h"
|
||||
|
||||
class WifiProps : public Props
|
||||
{
|
||||
public:
|
||||
WifiProps(const char*, const char*, const char*, const char*, const int);
|
||||
void begin(void(*)(String) = NULL);
|
||||
|
||||
private:
|
||||
WiFiClient _wifiClient;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user