Skip to content

Commit b837bd2

Browse files
committed
Application note content update
1 parent 28200f4 commit b837bd2

File tree

4 files changed

+46
-17
lines changed

4 files changed

+46
-17
lines changed
Loading

content/hardware/04.pro/boards/portenta-x8/tutorials/16.edge-ai-docker-container/content.md

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ CLIENT_ID = os.getenv("CLIENT_ID")
776776
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
777777
THING_ID = os.getenv("THING_ID")
778778
FLOWRATE_VARIABLE_ID = os.getenv("FLOWRATE_VARIABLE_ID")
779+
CLASSIFICATION_VARIABLE_ID = os.getenv("CLASSIFICATION_VARIABLE_ID") # New variable for classification
779780

780781
# M4 proxy settings
781782
M4_PROXY_HOST = os.getenv("M4_PROXY_HOST", "m4proxy")
@@ -785,45 +786,69 @@ M4_PROXY_PORT = int(os.getenv("M4_PROXY_PORT", "5001"))
785786
m4_proxy = Address(M4_PROXY_HOST, M4_PROXY_PORT)
786787

787788
def get_sensor_data():
789+
"""
790+
Retrieve the flow rate from the M4 via RPC.
791+
"""
788792
client = Client(m4_proxy)
789-
return [client.call("flow_rate")]
793+
try:
794+
return client.call("flow_rate")
795+
except Exception as e:
796+
print(f"Error retrieving sensor data: {e}")
797+
return None
790798

791-
def update_arduino_cloud(value):
792-
url = f"https://api2.arduino.cc/iot/v2/things/{THING_ID}/properties/{FLOWRATE_VARIABLE_ID}/publish"
799+
def update_arduino_cloud(variable_id, value):
800+
"""
801+
Send data to Arduino Cloud.
802+
"""
803+
url = f"https://api2.arduino.cc/iot/v2/things/{THING_ID}/properties/{variable_id}/publish"
793804
headers = {
794805
"Content-Type": "application/json",
795-
"Authorization": f"Bearer {CLIENT_ID}"
806+
"Authorization": f"Bearer {CLIENT_ID}" # Ensure valid token
796807
}
797808
payload = {"value": value}
798809

799810
try:
800811
response = requests.put(url, headers=headers, json=payload)
801812
if response.status_code == 200:
802-
print(f"Updated Arduino Cloud: Flow Rate = {value} L/min")
813+
print(f"Updated Arduino Cloud: {variable_id} = {value}")
803814
else:
804-
print(f"Failed to update Arduino Cloud: {response.status_code}, {response.text}")
815+
print(f"Failed to update {variable_id}: {response.status_code}, {response.text}")
805816
except requests.exceptions.RequestException as e:
806817
print(f"Error updating Arduino Cloud: {e}")
807818

808819
def classify_flow(host, port):
820+
"""
821+
Collects flow rate data, sends it for classification, and updates Arduino Cloud.
822+
"""
809823
url = f"http://{host}:{port}/api/features"
810824

811825
while True:
812826
data = {"features": [], "model_type": "int8"}
827+
828+
# Collect 100 sensor readings
813829
for _ in range(100):
814-
data["features"].extend(get_sensor_data())
830+
flow_rate = get_sensor_data()
831+
832+
if flow_rate is not None:
833+
data["features"].append(flow_rate)
834+
update_arduino_cloud(FLOWRATE_VARIABLE_ID, flow_rate) # Send flow rate to Arduino Cloud
835+
815836
time.sleep(0.1)
816837

817-
response = requests.post(url, json=data)
818-
if response.status_code == 200:
819-
classification = response.json().get("result", {}).get("classification", {})
820-
print(f"Classification: {classification}")
821-
822-
if classification:
823-
label = max(classification, key=classification.get)
824-
update_arduino_cloud(label) # Send classification to Arduino Cloud
825-
else:
826-
print(f"Failed classification: {response.status_code}")
838+
# Send collected data for classification
839+
try:
840+
response = requests.post(url, json=data)
841+
if response.status_code == 200:
842+
classification = response.json().get("result", {}).get("classification", {})
843+
print(f"Classification: {classification}")
844+
845+
if classification:
846+
label = max(classification, key=classification.get) # Get highest confidence label
847+
update_arduino_cloud(CLASSIFICATION_VARIABLE_ID, label) # Send classification to Arduino Cloud
848+
else:
849+
print(f"Failed classification: {response.status_code}, {response.text}")
850+
except requests.exceptions.RequestException as e:
851+
print(f"Error sending classification request: {e}")
827852

828853
if __name__ == "__main__":
829854
classify_flow("localhost", 1337)
@@ -837,6 +862,7 @@ CLIENT_SECRET=cloud_api_secret
837862
THING_ID=cloud_thing_id
838863
SPACE_ID=cloud_space_id
839864
FLOWRATE_VARIABLE_ID=cloud_flowrate_key
865+
CLASSIFICATION_VARIABLE_ID=cloud_classification_key
840866
```
841867

842868
The `docker-compose` file is updated to include Arduino Cloud credentials:
@@ -862,6 +888,7 @@ services:
862888
CLIENT_SECRET: ${CLIENT_SECRET}
863889
THING_ID: ${THING_ID}
864890
FLOWRATE_VARIABLE_ID: ${FLOWRATE_VARIABLE_ID}
891+
CLASSIFICATION_VARIABLE_ID: ${CLASSIFICATION_VARIABLE_ID}
865892
volumes:
866893
- "/tmp:/tmp"
867894
extra_hosts:
@@ -908,6 +935,8 @@ docker compose logs -f -n 10
908935

909936
Once deployed, the system will begin to get flow sensor data, classify it using the trained model and send the results to Arduino Cloud for monitoring and anomaly tracking.
910937

938+
![Flow data visualization with Arduino Cloud](assets/arduino-cloud-visualization-example.gif)
939+
911940
This integration allows real-time anomaly detection and cloud-based monitoring, combining edge inference on Portenta X8 with Arduino Cloud analytics. Users can remotely track flow rate anomalies, set up alerts and analyze historical trends to improve predictive maintenance of the system's point of interest.
912941

913942
## Additional Resources

0 commit comments

Comments
 (0)