Skip to content

Commit 2d0e65c

Browse files
committed
Revamp Mariquita with more structure (#1)
1 parent 232d5ae commit 2d0e65c

File tree

8 files changed

+432
-170
lines changed

8 files changed

+432
-170
lines changed

README.md

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Mariquita
2+
3+
Mariquita is a virtual device for Arduino IoT Cloud for testing.
4+
5+
```
6+
$ mariquita ping -u "<deviceId>" -p "<secret>" -t <thing ID>>
7+
Connected to Arduino IoT Cloud
8+
Subscribed true
9+
Property value sent successfully 81
10+
Property value sent successfully 87
11+
```
12+
13+
## Requirements
14+
15+
This is all you need to use Mariquita:
16+
* A "Generic ESP8266 Module" device in IoT Cloud (requires a Maker plan)
17+
* A thing with a `counter` property connected to the "Generic ESP8266 Module" device
18+
19+
## How to set up the device and thing in IoT Cloud
20+
21+
### Device
22+
23+
* Visit https://create.arduino.cc/iot/devices and select "Add device".
24+
* Select "Set up a 3rd party device".
25+
* Select "ESP8266".
26+
* From the drop down select "Generic ESP8266 Module", and click "Continue".
27+
* Pick a nice and friendly device name.
28+
* Save the "Device ID" and "Secret Key" is a safe place, because you will not be able to see them anymore.
29+
30+
### Thing ID
31+
32+
* Visit https://create.arduino.cc/iot/things and select "Create Thing".
33+
* Select "Add Variable".
34+
* Give the variable the name "counter", type "Integer Number" and leave the variable permission the value "Read & Write".
35+
* Press the "Add Variable" button to confirm.
36+
* Copy the "Thing ID" from the bottom right of the page.
37+
38+
### Connect the device and the thing
39+
40+
You should connect the new device to the new thing.
41+
42+
### Testing
43+
44+
```shell
45+
$ mariquita ping -u "<Device ID>" -p "<Secret Key>" -t <Thing ID>>
46+
```
47+
48+
If every works as expected you should see something similar to this output:
49+
```
50+
Connected to Arduino IoT Cloud
51+
Subscribed true
52+
Property value sent successfully 81
53+
Property value sent successfully 87
54+
```
55+
56+
If you visit https://create.arduino.cc/iot/devices the "Generic ESP8266 Module" device status should be "Online".

adapters/mqtt/adapter.go

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package mqtt
2+
3+
import (
4+
"crypto/tls"
5+
"fmt"
6+
7+
paho "github.com/eclipse/paho.mqtt.golang"
8+
)
9+
10+
type Adapter struct {
11+
host string
12+
clientID string
13+
username string
14+
password string
15+
useSSL bool
16+
autoReconnect bool
17+
client paho.Client
18+
qos int
19+
}
20+
21+
func NewAdapterWithAuth(host, clientID, username, password string) *Adapter {
22+
return &Adapter{
23+
host: host,
24+
clientID: clientID,
25+
username: username,
26+
password: password,
27+
useSSL: true,
28+
autoReconnect: true,
29+
}
30+
}
31+
32+
func (a *Adapter) Connect() (err error) {
33+
a.client = paho.NewClient(a.createClientOptions())
34+
if token := a.client.Connect(); token.Wait() && token.Error() != nil {
35+
err = token.Error()
36+
}
37+
return
38+
}
39+
40+
func (a *Adapter) Disconnect() (err error) {
41+
if a.client != nil {
42+
a.client.Disconnect(500)
43+
}
44+
return
45+
}
46+
47+
func (a *Adapter) Publish(topic string, message []byte) error {
48+
_, err := a.PublishWithQOS(topic, a.qos, message)
49+
return err
50+
}
51+
52+
func (a *Adapter) PublishWithQOS(topic string, qos int, message []byte) (paho.Token, error) {
53+
if a.client == nil {
54+
return nil, fmt.Errorf("MQTT client is nil")
55+
}
56+
57+
token := a.client.Publish(topic, byte(qos), false, message)
58+
return token, nil
59+
}
60+
61+
62+
func (a *Adapter) On(topic string, f func(message paho.Message)) (bool, error) {
63+
_, err := a.OnWithQOS(topic, a.qos, f)
64+
if err != nil {
65+
return false, err
66+
}
67+
return true, err
68+
}
69+
70+
func (a *Adapter) OnWithQOS(topic string, qos int, f func(message paho.Message)) (paho.Token, error) {
71+
if a.client == nil {
72+
return nil, fmt.Errorf("MQTT client is nil")
73+
}
74+
75+
token := a.client.Subscribe(topic, byte(qos), func(client paho.Client, msg paho.Message) {
76+
f(msg)
77+
})
78+
return token, nil
79+
80+
}
81+
82+
func (a *Adapter) createClientOptions() *paho.ClientOptions {
83+
opts := paho.NewClientOptions()
84+
opts.AddBroker(a.host)
85+
opts.SetClientID(a.clientID)
86+
opts.SetUsername(a.username)
87+
opts.SetPassword(a.password)
88+
89+
if a.useSSL {
90+
opts.SetTLSConfig(a.newTLSConfig())
91+
}
92+
return opts
93+
}
94+
95+
func (a *Adapter) newTLSConfig() *tls.Config {
96+
return &tls.Config{
97+
MinVersion: tls.VersionTLS11,
98+
MaxVersion: tls.VersionTLS12,
99+
ClientAuth: tls.NoClientCert,
100+
}
101+
}

command/ping.go

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package command
2+
3+
import (
4+
"fmt"
5+
paho "github.com/eclipse/paho.mqtt.golang"
6+
"github.com/spf13/cobra"
7+
"github.com/zmoog/mariquita/adapters/mqtt"
8+
"github.com/zmoog/mariquita/internal/properties"
9+
"math/rand"
10+
"os"
11+
"os/signal"
12+
"syscall"
13+
"time"
14+
)
15+
16+
var (
17+
host string
18+
username string
19+
password string
20+
thingID string
21+
)
22+
23+
func init() {
24+
pingCommand.Flags().StringVarP(&host, "host", "b", "tcps://mqtts-up.iot.arduino.cc:8884", "Broker endpoint (required)")
25+
pingCommand.Flags().StringVarP(&username, "username", "u", "", "Username (required)")
26+
pingCommand.Flags().StringVarP(&password, "password", "p", "", "Password (required)")
27+
pingCommand.Flags().StringVarP(&thingID, "thing_id", "t", "", "Thing ID (required)")
28+
29+
pingCommand.MarkFlagRequired("username")
30+
pingCommand.MarkFlagRequired("password")
31+
pingCommand.MarkFlagRequired("thing_id")
32+
33+
RootCmd.AddCommand(pingCommand)
34+
}
35+
36+
var pingCommand = &cobra.Command{
37+
Use: "ping",
38+
Short: "Ping Arduino IoT Cloud",
39+
Long: "Ping Arduino IoT Cloud",
40+
RunE: func(cmd *cobra.Command, args []string) error {
41+
mqttAdapter := mqtt.NewAdapterWithAuth(
42+
host,
43+
username,
44+
username,
45+
password,
46+
)
47+
48+
err := mqttAdapter.Connect()
49+
if err != nil {
50+
return err
51+
}
52+
fmt.Println("Connected to Arduino IoT Cloud")
53+
54+
inboundTopic := fmt.Sprintf("/a/t/%s/e/i", thingID)
55+
outboundTopic := fmt.Sprintf("/a/t/%s/e/o", thingID)
56+
57+
// Subscribing to the thing inbound topic to received new properties
58+
// values from the cloud.
59+
ok, _ := mqttAdapter.On(inboundTopic, func(msg paho.Message) {
60+
fmt.Println("received a message", msg)
61+
})
62+
fmt.Println("Subscribed", ok)
63+
64+
// Sending new random values (in the 0-100 range) to the thing specified
65+
// using the flags
66+
go func() {
67+
for {
68+
randomValue := rand.Intn(100)
69+
70+
property, err := properties.NewInteger("counter", randomValue)
71+
if err != nil {
72+
fmt.Println("Failed to encode property value", err)
73+
}
74+
75+
// Publishing a new random value to the outbound topic of the thing
76+
err = mqttAdapter.Publish(outboundTopic, property)
77+
if err != nil {
78+
fmt.Println("Failed to send property to Arduino IoT Cloud", err)
79+
}
80+
fmt.Println("Property value sent successfully", randomValue)
81+
82+
wait := 3
83+
time.Sleep(time.Duration(wait) * time.Second)
84+
}
85+
}()
86+
87+
// Wait for a sigterm signal (CTRL+C) to disconnect from the broker
88+
// and say good bye
89+
c := make(chan os.Signal, 1)
90+
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
91+
92+
// blocking here waiting for a signal from the terminal 😪
93+
<-c
94+
95+
mqttAdapter.Disconnect()
96+
if err != nil {
97+
return err
98+
}
99+
fmt.Println("Disconnected from Arduino IoT Cloud.")
100+
fmt.Println("Completed successfully.")
101+
102+
return nil
103+
},
104+
}

command/root.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package command
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var RootCmd = &cobra.Command{}
11+
12+
func Execute() {
13+
if err := RootCmd.Execute(); err != nil {
14+
fmt.Fprintln(os.Stderr, err)
15+
}
16+
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/eclipse/paho.mqtt.golang v1.3.2
88
github.com/juju/errors v0.0.0-20200330140219-3fe23663418f
99
github.com/sirupsen/logrus v1.8.1
10+
github.com/spf13/cobra v1.1.3
1011
)
1112

1213
replace github.com/cisco/senml => github.com/bcmi-labs/senml v0.1.0

0 commit comments

Comments
 (0)