@@ -776,6 +776,7 @@ CLIENT_ID = os.getenv("CLIENT_ID")
776
776
CLIENT_SECRET = os.getenv(" CLIENT_SECRET" )
777
777
THING_ID = os.getenv(" THING_ID" )
778
778
FLOWRATE_VARIABLE_ID = os.getenv(" FLOWRATE_VARIABLE_ID" )
779
+ CLASSIFICATION_VARIABLE_ID = os.getenv(" CLASSIFICATION_VARIABLE_ID" ) # New variable for classification
779
780
780
781
# M4 proxy settings
781
782
M4_PROXY_HOST = os.getenv(" M4_PROXY_HOST" , " m4proxy" )
@@ -785,45 +786,69 @@ M4_PROXY_PORT = int(os.getenv("M4_PROXY_PORT", "5001"))
785
786
m4_proxy = Address(M4_PROXY_HOST , M4_PROXY_PORT )
786
787
787
788
def get_sensor_data ():
789
+ """
790
+ Retrieve the flow rate from the M4 via RPC.
791
+ """
788
792
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
790
798
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 "
793
804
headers = {
794
805
" Content-Type" : " application/json" ,
795
- " Authorization" : f " Bearer { CLIENT_ID } "
806
+ " Authorization" : f " Bearer { CLIENT_ID } " # Ensure valid token
796
807
}
797
808
payload = {" value" : value}
798
809
799
810
try :
800
811
response = requests.put(url, headers = headers, json = payload)
801
812
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} " )
803
814
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} " )
805
816
except requests.exceptions.RequestException as e:
806
817
print (f " Error updating Arduino Cloud: { e} " )
807
818
808
819
def classify_flow (host , port ):
820
+ """
821
+ Collects flow rate data, sends it for classification, and updates Arduino Cloud.
822
+ """
809
823
url = f " http:// { host} : { port} /api/features "
810
824
811
825
while True :
812
826
data = {" features" : [], " model_type" : " int8" }
827
+
828
+ # Collect 100 sensor readings
813
829
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
+
815
836
time.sleep(0.1 )
816
837
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} " )
827
852
828
853
if __name__ == " __main__" :
829
854
classify_flow(" localhost" , 1337 )
@@ -837,6 +862,7 @@ CLIENT_SECRET=cloud_api_secret
837
862
THING_ID=cloud_thing_id
838
863
SPACE_ID=cloud_space_id
839
864
FLOWRATE_VARIABLE_ID=cloud_flowrate_key
865
+ CLASSIFICATION_VARIABLE_ID=cloud_classification_key
840
866
```
841
867
842
868
The ` docker-compose ` file is updated to include Arduino Cloud credentials:
@@ -862,6 +888,7 @@ services:
862
888
CLIENT_SECRET: ${CLIENT_SECRET}
863
889
THING_ID: ${THING_ID}
864
890
FLOWRATE_VARIABLE_ID: ${FLOWRATE_VARIABLE_ID}
891
+ CLASSIFICATION_VARIABLE_ID: ${CLASSIFICATION_VARIABLE_ID}
865
892
volumes:
866
893
- "/tmp:/tmp"
867
894
extra_hosts:
@@ -908,6 +935,8 @@ docker compose logs -f -n 10
908
935
909
936
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.
910
937
938
+ ![ Flow data visualization with Arduino Cloud] ( assets/arduino-cloud-visualization-example.gif )
939
+
911
940
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.
912
941
913
942
## Additional Resources
0 commit comments