Explorar o código

Converted Waveshare relays to operate as latched devices

frankvandenbos hai 1 mes
pai
achega
27873046f1

+ 10 - 7
Arduino/Digital Key v1/MAIN_ALL/MAIN_ALL.ino

@@ -1,14 +1,13 @@
 #include <HardwareSerial.h>
 #include <Preferences.h>
-//#include <iostream>
-//#include <sstream>
-//#include <stdio.h>
-//#include <string.h>
 #include <WiFi.h>
+#include <MacroDebugger.h>
 
 #include "WS_Bluetooth.h"
 #include "WS_GPIO.h"
 
+//#define ENABLE_DEBUG /* <-- Commenting this line will remove any trace of debug printing */
+
 /********************************************************  Data Analysis  ********************************************************/
 
 unsigned long timers[6] = { 0, 0, 0, 0, 0, 0 };
@@ -91,14 +90,12 @@ bool GetRelay(int relay)
   return RelayStatus(relay);
 }
 
-
 int i = 0;
 
 /********************************************************  Initializing  ********************************************************/
 void setup() 
 {
-  Serial.begin(115200);
-  Serial.println("hi there");
+  DEBUG_BEGIN();
   
   WiFi.mode(WIFI_STA);
   macAddress = WiFi.macAddress().c_str();
@@ -133,9 +130,15 @@ void loop()
   }
 
   unsigned long ms = millis();
+  bool bChanged = false;
   for (int relay=0; relay<6; relay++)
   {
     if ((timers[relay] > 0L) && (timers[relay] <= ms))
+    {
       SetRelay(relay,0L);
+      bChanged = true;
+    }
   }
+  if (bChanged && !IsConnected())
+    UpdateAdvertising();
 }

+ 117 - 44
Arduino/Digital Key v1/MAIN_ALL/WS_Bluetooth.cpp

@@ -1,16 +1,23 @@
+#include "esp_gap_ble_api.h"
 #include "BLEUUID.h"
 #include "BLEAdvertising.h"
 #include "WS_Bluetooth.h"
+#include "WS_GPIO.h"
 #include <ArduinoJson.h>
+#include <MacroDebugger.h>
 
 BLEServer* pServer;
 
+
 BLEService* pConfigService;
 BLECharacteristic* pConfigCharacteristic;
 
 BLEService* pControlService;
 BLECharacteristic* pControlCharacteristic;
 
+BLEAdvertising *pAdvertising;
+
+
 bool requiresrestart = false;
 bool isBluetoothConnected = false;
 
@@ -18,35 +25,25 @@ bool isBluetoothConnected = false;
 
 class ServerCallbacks : public BLEServerCallbacks {
                              
-  void onConnect(BLEServer* pServer) {                                        
-    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();   
-    pAdvertising->stop();
-    isBluetoothConnected = true;  
+  void onConnect(BLEServer* pServer) {
+    DEBUGLN("BLE::Connect()");                                           
+    //pAdvertising->stop();
+    isBluetoothConnected = true; 
   }
 
   void onDisconnect(BLEServer* pServer) {                              
 
-    isBluetoothConnected = false;
-
-    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();  
+    DEBUGLN("BLE::Disconnect()");
 
-    // String devicename = GetDeviceName().substring(0,30);
-    // BLEAdvertisementData data;
-    // data.setName(devicename);
+    isBluetoothConnected = false;
 
-    // pAdvertising->addServiceUUID(CONFIG_SERVICE_UUID);   
+    // BLEDevice::startAdvertising();                                               
+    // pConfigCharacteristic->notify();  
     // if (pControlService)   
-    //   pAdvertising->addServiceUUID(pControlService->getUUID());
-
-    // pAdvertising->setScanResponse(true);                                        
-    // pAdvertising->setMinPreferred(0x06);                                        
-    // pAdvertising->setMinPreferred(0x12);                                        
+    //   pControlCharacteristic->notify();                                                
+    // pAdvertising->start();  
 
-    BLEDevice::startAdvertising();                                               
-    pConfigCharacteristic->notify();  
-    if (pControlService)   
-      pControlCharacteristic->notify();                                                
-    pAdvertising->start();  
+    UpdateAdvertising();
 
     if (requiresrestart)
       Restart();
@@ -57,29 +54,24 @@ class ServerCallbacks : public BLEServerCallbacks {
 class ConfigCharacteristicCallback : public BLECharacteristicCallbacks 
 {
   
-  // char json[] = "{ \"warning\" : "", \"contact\" : "" }"
   void onRead(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) 
   { 
-    Serial.println("into writeConfig");
+  
+    DEBUGLN("Config::Read()");
     
     JsonDocument doc;
     doc["warning"] = GetWarningNotice();
     doc["contact"] = GetContactInfo();
     String json = "";
     serializeJson(doc,json);
-
-    Serial.println("serialised");
-    Serial.println(json);
     pCharacteristic->setValue(json);  
-    Serial.println("done");
   }
 
-// char json[] = "{ \"name\" : "PRS Digital Key", \"warning\" : "", \"contact\" : "" \"id\" : "00000000-0000-0000-0000-000000000000" }"
   void onWrite(BLECharacteristic* pCharacteristic) 
   {          
     String json = pCharacteristic->getValue();
     
-    Serial.println(json);
+    DEBUGLN(json);
     
     JsonDocument doc;
     deserializeJson(doc, json);
@@ -92,6 +84,7 @@ class ConfigCharacteristicCallback : public BLECharacteristicCallbacks
 
     pCharacteristic->setValue("");  
     requiresrestart = true;
+ 
   }
 
 };
@@ -99,8 +92,8 @@ class ConfigCharacteristicCallback : public BLECharacteristicCallbacks
 // char json[] = "{ \"Relay00\" : 15000, \"Relay01\" : 0, \"Relay02\" : 0, \"Relay03\" : 0, \"Relay04\" : 0, \"Relay05\" : 0 }"
 class ControlCharacteristicCallback : public BLECharacteristicCallbacks 
 {
-  // char json[] = "{\"Relay01\"="15000"
-  void onWrite(BLECharacteristic* pCharacteristic) 
+
+  void onWrite(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t *param) 
   {          
     String json = pCharacteristic->getValue();
 
@@ -114,13 +107,13 @@ class ControlCharacteristicCallback : public BLECharacteristicCallbacks
       SetRelay(i,time);
     }
 
-    pCharacteristic->setValue("");  
+    pCharacteristic->setValue("");
   }
 
   // char json[] = "{ \"Relay00\" : true, \"Relay01\" : false, \"Relay02\" : false, \"Relay03\" : false, \"Relay04\" : false, \"Relay05\" : false }"
   void onRead(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) 
   { 
-    
+    DEBUGLN("Control::Read()");
     JsonDocument doc;
     char name[10];
     for (int i=0; i<6; i++)
@@ -140,13 +133,90 @@ bool IsConnected()
   return isBluetoothConnected;
 }
 
+void UpdateAdvertising()
+{
+    DEBUGLN("UpdateAdvertising()");
+    pAdvertising->stop();
+    BLEAdvertisementData advData;
+    advData.addData(UUID32ToAdvertisingData(0xce6c0b18));
+    if (pControlService)
+      advData.addData(UUID128ToAdvertisingData(pControlService->getUUID()));
+    advData.setManufacturerData(DeviceStatus());
+    pAdvertising->setAdvertisementData(advData);
+    pAdvertising->start();
+}
+
+String DeviceStatus()
+{
+  String result;
+  uint16_t companyID = 0x02E5;  // Espressif's Bluetooth SIG company ID
+  //uint16_t companyID = 0xFFFF;  // Deliberately invalid for identification purposes
+  result += (char)(companyID & 0xFF);        // LSB
+  result += (char)((companyID >> 8) & 0xFF); // MSB
+
+  byte status = 0;
+  if (RelayStatus(0))
+    status += 1;
+  if (RelayStatus(1))
+    status += 2;
+  if (RelayStatus(2))
+    status += 4;
+  if (RelayStatus(3))
+    status += 8;
+  if (RelayStatus(4))
+    status += 16;
+  if (RelayStatus(5))
+    status += 32;
+  result += (char)status;
+
+
+  return result;
+}
+
+String UUID128ToAdvertisingData(BLEUUID uuid128) {
+  String adField;
+
+  // Get raw 16 bytes of the 128-bit UUID
+  const uint8_t* uuidBytes = uuid128.getNative()->uuid.uuid128;
+
+  // First byte: length (16 bytes data + 1 byte type)
+  adField += (char)(16 + 1);
+
+  // Second byte: type = 0x07 (complete list of 128-bit UUIDs)
+  adField += (char)0x07;
+
+  // Append 16 UUID bytes
+  for (int i=0; i<16; i++) {
+    adField += (char)(uuidBytes[i] & 0xFF);
+  }
+
+  return adField;
+}
+
+String UUID32ToAdvertisingData(uint32_t uuid32) {
+  String adField;
+
+  // 4 bytes of UUID + 1 byte type = 5 bytes total payload length
+  adField += (char)(4 + 1);   // total length
+  adField += (char)0x05;      // 0x05 = Complete List of 32-bit Service Class UUIDs
+
+  // Append UUID in little-endian byte order
+  adField += (char)(uuid32 & 0xFF);
+  adField += (char)((uuid32 >> 8) & 0xFF);
+  adField += (char)((uuid32 >> 16) & 0xFF);
+  adField += (char)((uuid32 >> 24) & 0xFF);
+
+  return adField;
+}
+
 void Bluetooth_Init()
 {
   /*************************************************************************
   Bluetooth
   *************************************************************************/
   
-  String devicename = GetDeviceName().substring(0,30);
+  DEBUGLN("Initialising BLE..."); 
+  String devicename = GetDeviceName().substring(0,24);
   
   // Initialise the Device
   BLEDevice::init(devicename);
@@ -154,31 +224,31 @@ void Bluetooth_Init()
   pServer = BLEDevice::createServer();                                          
   pServer->setCallbacks(new ServerCallbacks());  
 
-  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();  
-
+  pAdvertising = BLEDevice::getAdvertising(); 
+  
   // Set up the Configuration interface (always)
   pConfigService = pServer->createService(CONFIG_SERVICE_UUID);  
+
   pConfigCharacteristic = pConfigService->createCharacteristic(CONFIG_CHARACTERISTIC_UUID,
     BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);       
   pConfigCharacteristic->setCallbacks(new ConfigCharacteristicCallback());
   pConfigService->start();
-  pAdvertising->addServiceUUID(CONFIG_SERVICE_UUID);                                   
-  
+  pAdvertising->addServiceUUID(CONFIG_SERVICE_UUID); 
+                    
   String equipmentid = GetEquipmentId();
   if (!equipmentid.isEmpty() && !equipmentid.equalsIgnoreCase("00000000-0000-0000-0000-000000000000"))
   {
-    BLEService* pControlService = pServer->createService(equipmentid); 
+    pControlService = pServer->createService(equipmentid); 
     pControlCharacteristic = pControlService->createCharacteristic(CONTROL_CHARACTERISTIC_UUID,
       BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);   
     pControlCharacteristic->setCallbacks(new ControlCharacteristicCallback());
     pControlService->start(); 
-    pAdvertising->addServiceUUID(equipmentid);        
+    pAdvertising->addServiceUUID(equipmentid); 
   }
-    
 
-  pAdvertising->setScanResponse(true);                                          
+  //pAdvertising->setScanResponse(true);                                          
   pAdvertising->setMinPreferred(0x06);                                          
-  pAdvertising->setMinPreferred(0x12);                                          
+  pAdvertising->setMinPreferred(0x12);                                      
 
   BLEDevice::startAdvertising();  
 
@@ -187,6 +257,9 @@ void Bluetooth_Init()
     pControlCharacteristic->notify();  
                                                     
   pAdvertising->start();
- 
+
+  UpdateAdvertising();
+  DEBUGLN("BLE Initialisation complete."); 
+
 
 }

+ 5 - 0
Arduino/Digital Key v1/MAIN_ALL/WS_Bluetooth.h

@@ -20,10 +20,15 @@ extern String GetEquipmentId();
 extern bool GetRelay(int relay);
 extern void SetRelay(int relay, long time);
 
+String DeviceStatus();
+String UUID128ToAdvertisingData(BLEUUID uuid128);
+String UUID32ToAdvertisingData(uint32_t uuid32);
+
 extern void Restart();
 
 void Bluetooth_Init();
 bool IsConnected();
 
+void UpdateAdvertising();
 
 #endif

+ 12 - 3
PRS.Avalonia/PRS.Avalonia/Modules/EquipmentModule/DigitalKeys/DigitalKeyListView.axaml

@@ -28,6 +28,14 @@
         <converters:GuidToBooleanConverter
             x:Key="IsDigitalKeyConverter"
             Inverted="True"/>
+     
+        <modules:DigitalKeyStatusColorConverter
+            x:Key="DigitalKeyStatusColorConverter"
+            />
+        
+        <modules:DigitalKeyStatusEnabledConverter
+            x:Key="DigitalKeyStatusEnabledConverter"
+        />
             
         <DataTemplate
             x:Key="DigitalKeyDisplayTemplate"
@@ -82,15 +90,16 @@
                 
                 <Button Classes="Standard"
                     Grid.Column="1"
-                    Margin="2,0,0,0"
+                    Margin="4,0,0,0"
                     Padding="0"
                     CornerRadius="4,50,50,4"
                     x:DataType="modules:DigitalKeyDisplay"
-                    Background="{Binding Id, Converter={StaticResource DigitalKeyColorConverter}}"
+                    Background="{Binding Status, Converter={StaticResource DigitalKeyStatusColorConverter}}"
                     Command="{Binding $parent[ItemsControl].((modules:DigitalKeyListViewModel)DataContext).StartCommand}"
                     CommandParameter="{Binding .}"
                     HorizontalContentAlignment="Stretch"
-                    VerticalContentAlignment="Stretch">
+                    VerticalContentAlignment="Stretch"
+                    IsEnabled="{Binding Status, Converter={StaticResource DigitalKeyStatusEnabledConverter}}">
                     <Grid>
                         <components:CircularCountdownTimer
                             Grid.Column="0"

+ 20 - 0
PRS.Avalonia/PRS.Avalonia/Modules/EquipmentModule/DigitalKeys/DigitalKeyListView.axaml.cs

@@ -1,9 +1,29 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using InABox.Avalonia.Converters;
 
 namespace PRS.Avalonia.Modules;
 
+public class DigitalKeyStatusColorConverter : AbstractConverter<byte?,IBrush>
+{
+    protected override IBrush? Convert(byte? value, object? parameter = null)
+    {
+        return (value ?? 0) == 0
+            ? Brushes.Red
+            : Brushes.LightGreen;
+    }
+}
+
+public class DigitalKeyStatusEnabledConverter : AbstractConverter<byte?,bool>
+{
+    protected override bool Convert(byte? value, object? parameter = null)
+    {
+        return value is not null;
+    }
+}
+
 public partial class DigitalKeyListView : UserControl
 {
     public DigitalKeyListView()

+ 47 - 41
PRS.Avalonia/PRS.Avalonia/Modules/EquipmentModule/DigitalKeys/DigitalKeyListViewModel.cs

@@ -35,18 +35,16 @@ public partial class DigitalKeyDisplay : ObservableObject
     [ObservableProperty] private byte[] _image;
     [ObservableProperty] private bool _active;
     [ObservableProperty] private bool _controlServiceID;
-
+    [ObservableProperty] private byte? _status;
 }
 
 public partial class DigitalKeyListViewModel : ModuleViewModel
 {
     public override string Title => "Digital Keys";
+
+    [ObservableProperty] private DigitalKeyDisplay[] _keys;
     
-    [ObservableProperty] 
-    private CoreObservableCollection<DigitalKeyDisplay> _keys = new();
-    
-    [ObservableProperty]
-    private EquipmentModel _equipment;
+    [ObservableProperty] private EquipmentModel _equipment;
 
     private IImage _question;
     
@@ -105,35 +103,34 @@ public partial class DigitalKeyListViewModel : ModuleViewModel
         {
             foreach (var device in PlatformTools.Bluetooth.Devices.ToArray())
             {
-                if (!device.AvailableServices.Any())
+                var equipment = Equipment.Items.FirstOrDefault(x => device?.AvailableServices.Contains(x.ID) ?? false);
+                if (equipment != null)
                 {
                     keys.Add(new DigitalKeyDisplay()
                     {
                         Device = device,
-                        Code = device.Name, 
-                        Description = device.ID, 
-                        Id = Guid.Empty
-                    });                    
+                        Id = equipment.ID,
+                        Code = equipment.Code,
+                        Description = equipment.Description,
+                        Image = equipment.Thumbnail,
+                        Status = device?.ManufacturerData?.FirstOrDefault()
+                    });
                 }
                 else
                 {
-                    var equipment = Equipment.Items.FirstOrDefault(x => device.AvailableServices.Contains(x.ID));
-                    if (equipment != null)
+                    keys.Add(new DigitalKeyDisplay()
                     {
-                        keys.Add(new DigitalKeyDisplay()
-                        {
-                            Device = device,
-                            Id = equipment.ID,
-                            Code = equipment.Code,
-                            Description = equipment.Description,
-                            Image = equipment.Thumbnail
-                        });
-                    }
+                        Device = device,
+                        Id = Guid.Empty,
+                        Code = device?.Name ?? "PRS Digital Key",
+                        Description = device?.ID ?? "Unknown ID",
+                        Status = null
+                    });
                 }
-
             }
         }
-        Keys.ReplaceRange(keys.OrderBy(x=>x.Code));
+        Console.WriteLine($"{DateTime.Now:hh:mm:ss} DigitalKeyListVideModel:Updating Keys ({keys.Count})");
+        Keys = keys.OrderBy(x=>x.Code).ToArray();
     }
 
     protected override async Task<TimeSpan> OnRefresh()
@@ -147,7 +144,7 @@ public partial class DigitalKeyListViewModel : ModuleViewModel
 
     private bool bActive;
 
-    private IConnectedBluetoothDevice? _connectedBluetoothDevice = null;
+    //private IConnectedBluetoothDevice? _connectedBluetoothDevice = null;
     
     [RelayCommand]
     private async Task Start(DigitalKeyDisplay shell)
@@ -155,25 +152,34 @@ public partial class DigitalKeyListViewModel : ModuleViewModel
         if (shell.Id == Guid.Empty)
             return;
         bActive = true;
-        _connectedBluetoothDevice = await PlatformTools.Bluetooth.Connect(shell.Device);
-        if (_connectedBluetoothDevice != null)
+        using (var _connectedBluetoothDevice = await PlatformTools.Bluetooth.Connect(shell.Device))
         {
-            try
-            {
-                shell.Active = true;
-                var cmd = new Dictionary<string, long>() { { "Relay00", 15000 },  { "Relay01", 0 } , { "Relay02", 0 } , { "Relay03", 0 } , { "Relay4", 0 },  { "Relay05", 0 }  };
-                var json = Serialization.Serialize(cmd);
-                await _connectedBluetoothDevice.WriteStringAsync(shell.Id, PlatformTools.DigitalKeyControlId, json);
-                await Task.Delay(15000);
-            }
-            catch (Exception e)
+            shell.Status = null;
+            if (_connectedBluetoothDevice != null)
             {
-                Logger.Send(LogType.Error,"",e.Message);
+                try
+                {
+                    var isactive = (_connectedBluetoothDevice.ManufacturerData?.FirstOrDefault() ?? 0) > 0;
+                    var cmd = new Dictionary<string, long>()
+                    {
+                        { "Relay00", (isactive ? 0 : -1) }, { "Relay01", 0 }, { "Relay02", 0 }, { "Relay03", 0 },
+                        { "Relay4", 0 }, { "Relay05", 0 }
+                    };
+                    var json = Serialization.Serialize(cmd);
+                    await _connectedBluetoothDevice.WriteStringAsync(shell.Id, PlatformTools.DigitalKeyControlId, json);
+                    await Task.Delay(1000);
+                }
+                catch (Exception e)
+                {
+                    Logger.Send(LogType.Error, "", e.Message);
+                }
+
+                await PlatformTools.Bluetooth.Disconnect(_connectedBluetoothDevice);
+                //_connectedBluetoothDevice.Dispose();
+                //_connectedBluetoothDevice = null;
+                bActive = false;
+                OnPropertyChanged(nameof(Keys));
             }
-            await PlatformTools.Bluetooth.Disconnect(_connectedBluetoothDevice);
-            _connectedBluetoothDevice.Dispose();
-            _connectedBluetoothDevice = null;
-            bActive = false;
         }
     }
     

+ 1 - 1
PRS.DigitalKey/PRS.DigitalKey/DigitalKeys/DigitalKeysViewModel.cs

@@ -75,7 +75,7 @@ public partial class DigitalKeysViewModel : ViewModelBase
         {
             foreach (var device in PlatformTools.Bluetooth.Devices.ToArray())
             {
-                if (device.AvailableServices.Any())
+                if (device?.AvailableServices.Any() == true)
                 {
                     var id = device.AvailableServices.First();
                     keys.Add(