Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 661683f

Browse files
committedJul 9, 2024·
Added ota apply
1 parent 63bc776 commit 661683f

12 files changed

+267
-3
lines changed
 
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include <ArduinoIoTCloud.h>
2+
3+
#include "thingProperties.h"
4+
5+
void setup() {
6+
// Initialize serial and wait for port to open:
7+
Serial.begin(9600);
8+
// This delay gives the chance to wait for a Serial Monitor without blocking if none is found
9+
delay(1500);
10+
11+
// Defined in thingProperties.h
12+
initProperties();
13+
14+
// Connect to Arduino IoT Cloud
15+
ArduinoCloud.begin(ArduinoIoTPreferredConnection, false, "mqtts-sa.iot.oniudra.cc");
16+
17+
setDebugMessageLevel(4);
18+
ArduinoCloud.printDebugInfo();
19+
}
20+
21+
unsigned long previousMillis = 0;
22+
const long interval = 5000; //ms
23+
bool increase = true;
24+
25+
// the loop function runs over and over again forever
26+
void loop() {
27+
ArduinoCloud.update();
28+
29+
unsigned long currentMillis = millis();
30+
if (currentMillis - previousMillis >= interval) {
31+
previousMillis = currentMillis;
32+
increase = !increase;
33+
34+
if(pressure < 2){
35+
pressure = 8;
36+
}
37+
if(temperature < 2){
38+
temperature = 25;
39+
}
40+
41+
int randNumber = random(10.0, 70.0);
42+
if(!increase){
43+
randNumber = -randNumber;
44+
}
45+
float diff = (float)randNumber / 100.0;
46+
47+
pressure = pressure + diff;
48+
temperature = temperature + diff;
49+
50+
Serial.println(AIOT_CONFIG_LIB_VERSION);
51+
Serial.println("2.1");
52+
}
53+
54+
}
55+
56+
/*
57+
Since Temperature is READ_WRITE variable, onTemperatureChange() is
58+
executed every time a new value is received from IoT Cloud.
59+
*/
60+
void onTemperatureChange() {
61+
// Add your code here to act upon Temperature change
62+
}
63+
64+
/*
65+
Since Pressure is READ_WRITE variable, onPressureChange() is
66+
executed every time a new value is received from IoT Cloud.
67+
*/
68+
void onPressureChange() {
69+
// Add your code here to act upon Pressure change
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"cpu": {
3+
"fqbn": "arduino:mbed_nano:nanorp2040connect",
4+
"name": "",
5+
"type": "serial"
6+
},
7+
"secrets": [
8+
{
9+
"name": "SECRET_SSID",
10+
"value": ""
11+
},
12+
{
13+
"name": "SECRET_OPTIONAL_PASS",
14+
"value": ""
15+
}
16+
],
17+
"included_libs": []
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Code generated by Arduino IoT Cloud, DO NOT EDIT.
2+
3+
#include <ArduinoIoTCloud.h>
4+
#include <Arduino_ConnectionHandler.h>
5+
6+
const char SSID[] = SECRET_SSID; // Network SSID (name)
7+
const char PASS[] = SECRET_OPTIONAL_PASS; // Network password (use for WPA, or use as key for WEP)
8+
9+
void onPressureChange();
10+
void onTemperatureChange();
11+
12+
float pressure;
13+
CloudTemperature temperature;
14+
15+
void initProperties(){
16+
17+
ArduinoCloud.addProperty(pressure, READWRITE, 30 * SECONDS, onPressureChange);
18+
ArduinoCloud.addProperty(temperature, READWRITE, 30 * SECONDS, onTemperatureChange);
19+
20+
}
21+
22+
WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);
23+

‎TT/resources/thing_1.yaml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
id: tositewise-3-2547e
2+
name: ToSiteWise 3
3+
sketch_template: ToSiteWise 3
4+
sketch_name: ToSiteWise_3_may08a
5+
timezone: ""
6+
tags:
7+
- key: integration
8+
value: sitewise
9+
variables:
10+
- id: pressure
11+
name: pressure
12+
type: FLOAT
13+
permission: READ_WRITE
14+
update_parameter: 30
15+
update_strategy: TIMED
16+
variable_name: pressure
17+
- id: temperature
18+
name: temperature
19+
type: TEMPERATURE
20+
permission: READ_WRITE
21+
update_parameter: 30
22+
update_strategy: TIMED
23+
variable_name: temperature

‎TT/template.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"title":"OK - RP with binaries","name":"OK - RP with binaries","description":"\u003cp\u003eBIN CONTENT INCLUDED\u003c/p\u003e","template":{"things":["thing_1.yaml"],"dashboards":null,"triggers":null},"resources":{"things":1,"dashboards":0,"triggers":0,"max_variables":2},"compatible_boards":[{"fqbn":"arduino:mbed_nano:nanorp2040connect"}],"images":[],"assets":{"thing_template_names":["ToSiteWise 3"],"dashboard_template_names":[],"trigger_template_names":[]},"source_template_id":"af076825-c0d2-4a27-a1cd-042df3604bae"}

‎TT/template_ok-rp-with-binaries.tino

248 KB
Binary file not shown.

‎cli/template/apply.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type applyFlags struct {
3434
templatePrefix string
3535
deviceId string
3636
netCredentials string
37+
applyOta bool
3738
}
3839

3940
func initTemplateApplyCommand() *cobra.Command {
@@ -54,6 +55,7 @@ func initTemplateApplyCommand() *cobra.Command {
5455
applyCommand.Flags().StringVarP(&flags.templatePrefix, "prefix", "p", "", "Prefix to apply to the name of created resources")
5556
applyCommand.Flags().StringVarP(&flags.deviceId, "device-id", "d", "", "Device ID")
5657
applyCommand.Flags().StringVarP(&flags.netCredentials, "network-credentials", "n", "", "Network credentials")
58+
applyCommand.Flags().BoolVarP(&flags.applyOta, "apply-ota", "o", false, "start ota with binary contained into template")
5759

5860
applyCommand.MarkFlagRequired("template-id")
5961
applyCommand.MarkFlagRequired("prefix")
@@ -72,8 +74,8 @@ func runTemplateApplyCommand(flags *applyFlags) error {
7274
if err != nil {
7375
return fmt.Errorf("parsing network credentials: %w", err)
7476
}
75-
76-
return template.ApplyCustomTemplates(cred, flags.templateId, flags.deviceId, flags.templatePrefix, deviceNetCredentials)
77+
78+
return template.ApplyCustomTemplates(cred, flags.templateId, flags.deviceId, flags.templatePrefix, deviceNetCredentials, flags.applyOta)
7779
}
7880

7981
func parseCredentials(credentials string) (map[string]string, error) {
Binary file not shown.

‎command/template/apply.go

+113-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,16 @@
1818
package template
1919

2020
import (
21+
"archive/zip"
2122
"context"
23+
"errors"
2224
"fmt"
25+
"io"
26+
"os"
2327
"strings"
2428

2529
"github.com/arduino/arduino-cli/cli/feedback"
30+
"github.com/arduino/arduino-cloud-cli/command/ota"
2631
"github.com/arduino/arduino-cloud-cli/config"
2732
"github.com/arduino/arduino-cloud-cli/internal/iot"
2833
storageapi "github.com/arduino/arduino-cloud-cli/internal/storage-api"
@@ -31,7 +36,9 @@ import (
3136
"github.com/sirupsen/logrus"
3237
)
3338

34-
func ApplyCustomTemplates(cred *config.Credentials, templateId, deviceId, prefix string, networkCredentials map[string]string) error {
39+
var errNoBinaryFound = errors.New("no binary found in the template")
40+
41+
func ApplyCustomTemplates(cred *config.Credentials, templateId, deviceId, prefix string, networkCredentials map[string]string, applyOta bool) error {
3542

3643
ctx := context.Background()
3744

@@ -73,6 +80,17 @@ func ApplyCustomTemplates(cred *config.Credentials, templateId, deviceId, prefix
7380
}
7481
feedback.Printf("Template applied successfully to device %s", deviceId)
7582

83+
if applyOta {
84+
// Now, start OTA with binary available in the template
85+
done, err := runOTAForTemplate(ctx, cred, templateId, deviceId, apiclient)
86+
if err != nil {
87+
return err
88+
}
89+
if done {
90+
feedback.Printf("OTA started successfully on device %s", deviceId)
91+
}
92+
}
93+
7694
return nil
7795
}
7896

@@ -87,6 +105,11 @@ func resolveDeviceNetworkConfigurations(ctx context.Context, cl *iot.Client, dev
87105
}
88106
logrus.Infof("Device %s - type: %s - connection-type: %s", deviceId, device.Type, *device.ConnectionType)
89107

108+
// Check if device is linked to a thing. In such case, block the operation.
109+
if device.Thing != nil && device.Thing.Id != "" {
110+
return nil, fmt.Errorf("device %s is already linked to a thing (thing_id: %s)", deviceId, device.Thing.Id)
111+
}
112+
90113
credentials, err := cl.DeviceNetworkCredentials(ctx, device.Type, *device.ConnectionType)
91114
if err != nil {
92115
return nil, err
@@ -123,3 +146,92 @@ func humanReadableCredentials(cred []iotclient.ArduinoCredentialsv1) string {
123146
}
124147
return buf.String()
125148
}
149+
150+
func runOTAForTemplate(ctx context.Context, cred *config.Credentials, templateId, deviceId string, apiclient *storageapi.StorageApiClient) (bool, error) {
151+
otaTempDir, err := os.MkdirTemp("cli-template-ota", "")
152+
if err != nil {
153+
return false, fmt.Errorf("%s: %w", "cannot create temporary folder", err)
154+
}
155+
defer func() {
156+
err := os.RemoveAll(otaTempDir)
157+
if err != nil {
158+
logrus.Warnf("Failed to remove temp directory: %v", err)
159+
}
160+
}()
161+
162+
filecreaed, err := apiclient.ExportCustomTemplate(templateId, otaTempDir)
163+
if err != nil {
164+
return false, err
165+
}
166+
167+
// open the file and be ready to send it to the device
168+
otaFile, err := extractBinary(filecreaed, otaTempDir)
169+
if err != nil {
170+
return false, err
171+
}
172+
if otaFile == "" {
173+
feedback.Printf("No binary OTA file found in the template")
174+
return false, nil
175+
}
176+
177+
// Upload the OTA file to the device
178+
err = ota.Upload(ctx, &ota.UploadParams{
179+
DeviceID: deviceId,
180+
File: otaFile,
181+
}, cred)
182+
if err != nil {
183+
return false, err
184+
}
185+
186+
return true, nil
187+
}
188+
189+
func extractBinary(filepath *string, tempDir string) (string, error) {
190+
zipFile, err := os.Open(*filepath)
191+
if err != nil {
192+
return "", err
193+
}
194+
defer zipFile.Close()
195+
196+
fileInfo, err := zipFile.Stat()
197+
if err != nil {
198+
return "", fmt.Errorf("failed to get file info: %w", err)
199+
}
200+
201+
zipReader, err := zip.NewReader(zipFile, fileInfo.Size())
202+
if err != nil {
203+
return "", fmt.Errorf("failed to open archive reader: %w", err)
204+
}
205+
206+
var binaryFile *zip.File
207+
for _, file := range zipReader.File {
208+
if strings.Contains(file.Name, "resources/binaries") {
209+
logrus.Debugf("binary OTA file from template: %s", file.Name)
210+
binaryFile = file
211+
break
212+
}
213+
}
214+
215+
if binaryFile != nil {
216+
// Extract content to a temporary file
217+
tempFile, err := os.CreateTemp(tempDir, "tmpl_bin_*.bin")
218+
if err != nil {
219+
return "", fmt.Errorf("failed to create temporary file: %w", err)
220+
}
221+
defer tempFile.Close()
222+
inputF, err := binaryFile.Open()
223+
if err != nil {
224+
return "", fmt.Errorf("failed to open file in archive: %w", err)
225+
}
226+
defer inputF.Close()
227+
228+
_, err = io.Copy(tempFile, inputF)
229+
if err != nil {
230+
return "", fmt.Errorf("failed to copy file content: %w", err)
231+
}
232+
233+
return tempFile.Name(), nil
234+
}
235+
236+
return "", errNoBinaryFound
237+
}

‎command/template/apply_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package template
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestTemplateLIsting(t *testing.T) {
10+
tmpDir := t.TempDir()
11+
path := ".testdata/template_ok-rp-with-binaries.tino"
12+
file, err := extractBinary(&path, tmpDir)
13+
assert.Nil(t, err)
14+
assert.NotNil(t, file)
15+
}

‎template_ok-rp-with-binaries.tino

248 KB
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.