Android硬件通信之 蓝牙Mesh通信

news2024/12/23 16:31:30

一,简介

蓝牙4.0以下称为传统蓝牙,4.0以上是低功耗蓝牙,5.0开始主打物联网

5.0协议蓝牙最重要的技术就是Mesh组网,实现1对多,多对多的无线通信。即从点对点传输发展为网络拓扑结构,主要领域如灯光控制等,可以同时控制一组内的多个设备。

如下模型,把灯具分组,就可以同时控制一组或者多组内的多台设备

二 蓝牙组网步骤 

2.1 扫描,还是用BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();

/**
 * Start scanning for Bluetooth devices.
 *
 * @param filterUuid UUID to filter scan results with
 */
public void startScan(final UUID filterUuid, boolean auto) {
	mFilterUuid = filterUuid;
	Log.e(TAG, "mScannerStateLiveData: 6" );
	if (mScannerStateLiveData.isScanning()) {
		return;
	}

	if (mFilterUuid.equals(BleMeshManager.MESH_PROXY_UUID)) {
		final MeshNetwork network = mMeshManagerApi.getMeshNetwork();
		if (network != null) {
			if (!network.getNetKeys().isEmpty()) {
				mNetworkId = mMeshManagerApi.generateNetworkId(network.getNetKeys().get(0).getKey());
			}
		}
	}
	Log.e(TAG, "mScannerStateLiveData: 7" );
	mScannerStateLiveData.scanningStarted();
	//Scanning settings
	final ScanSettings settings = new ScanSettings.Builder()
			.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
			// Refresh the devices list every second
			.setReportDelay(0)
			// Hardware filtering has some issues on selected devices
			.setUseHardwareFilteringIfSupported(false)
			// Samsung S6 and S6 Edge report equal value of RSSI for all devices. In this app we ignore the RSSI.
			/*.setUseHardwareBatchingIfSupported(false)*/
			.build();

	//Let's use the filter to scan only for unprovisioned mesh nodes.
	final List<ScanFilter> filters = new ArrayList<>();
	filters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid((filterUuid))).build());

	final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
	Log.e(TAG, "startScan: 开始扫描" );
	scanner.startScan(filters, settings, mScanCallbacks);
}

2.2 扫描到设备后,连接并获取地址

@SuppressLint("RestrictedApi")
private void connect(final ExtendedBluetoothDevice extendedBluetoothDevice) {

	/**
	 * 蓝牙     --根据UUID中的型号ID
	 * DMX      --根据品牌-系列-模式
	 * 2.4G     --单色温,双色温,全彩
	 */
	String uuid = ((UnprovisionedBeacon) selectedBluetoothDevice.getBeacon()).getUuid().toString().replace("-", "");
	String[] stringArray = OrderUtils.hexStringToStringArray(uuid);
	//型号byte 10 ~ 12	表示型号
	String modelFileName = stringArray[10] + stringArray[11] + stringArray[12];
	Log.e(TAG, "modelFileName: " + modelFileName);

	dataJson = dataJsonCommonDaoUtils.queryBykey(modelFileName);
	if (dataJson == null) {
		ConfirmDialog confirmDialog = new ConfirmDialog(mContext, R.style.dialog, "没有该型号的配置文件:\n" + modelFileName, getResources().getString(R.string.account_confirm));
		confirmDialog.show();
		hideCustomProgress();
		cancleHandlerProvisioning();
		return;
	}
	if (fromType == 1 || fromType == 2) {
		String json = dataJson.getDataJson();
		if (!json.contains("FUNCTION")) {
			ConfirmDialog confirmDialog = new ConfirmDialog(mContext, R.style.dialog, "请添加控制盒类型的配置文件\n", getResources().getString(R.string.account_confirm));
			confirmDialog.show();
			hideCustomProgress();
			cancleHandlerProvisioning();
			return;
		}
	}

	nextChBlue = getNextCHBlue();
	if (nextChBlue == -1) {//如果nextChBlue==-1,说明蓝牙地址1-512已将占完,直接return;
		cancleHandlerProvisioning();
		return;
	}

	showCustomProgress(getResources().getString(R.string.ble_provisioning) + (addIndex + 1) + "/" + mSelectCommonList.size());

	scannerViewModel.getScannerRepository().stopScan();

	provisioningViewModel.connect(this, extendedBluetoothDevice, false);

	//监听连接状态
	provisioningViewModel.getConnectionState().observe(this, new Observer<String>() {
		@Override
		public void onChanged(@Nullable String s) {
			Log.e(TAG, "getConnectionState: " + s);
		}
	});
	//监听是否连接
	provisioningViewModel.isConnected().observe(this, connected -> {
		final boolean isComplete = provisioningViewModel.isProvisioningComplete();
		if (isComplete) {
			return;
		}

		if (connected != null) {
			if (connected) {
				Log.e(TAG, "isConnected: " + "连接成功");
				handlerProvisioning.sendEmptyMessageDelayed(1, 4000);
			} else {
				Log.e(TAG, "isConnected: " + "连接失败");
			}
		} else {
			Log.e(TAG, "isConnected: " + "未连接");
		}

	});

	//监听设备信息
	provisioningViewModel.isDeviceReady().observe(this, deviceReady -> {
		if (provisioningViewModel.getBleMeshManager().isDeviceReady()) {
			Log.e(TAG, "isDeviceReady:");
			final boolean isComplete = provisioningViewModel.isProvisioningComplete();
			if (isComplete) {
				setupProvisionerStateObservers();
				return;
			}
		}
	});

	//监听重连
	provisioningViewModel.isReconnecting().observe(this, isReconnecting -> {
		Log.e(TAG, "isReconnecting:");
		if (isReconnecting != null && isReconnecting) {
			provisioningViewModel.getUnprovisionedMeshNode().removeObservers(this);
		} else {
			setResultIntent();
		}
	});

	//监听key
	provisioningViewModel.getNetworkLiveData().observe(this, meshNetworkLiveData -> {

		final ApplicationKey applicationKey = meshNetworkLiveData.getSelectedAppKey();
		Log.e(TAG, "getNetworkLiveData:Key:" + MeshParserUtils.bytesToHex(applicationKey.getKey(), false));
		Log.e(TAG, "getNetworkLiveData: Address:" + getString(R.string.ble_hex_format, String.format(Locale.US, "%04X", meshNetworkLiveData.getMeshNetwork().getUnicastAddress())));

		// 获取已选择的app key
		//appKeyView.setText(MeshParserUtils.bytesToHex(applicationKey.getKey(), false));
//            Log.e("TAG", "onCreate: " + MeshParserUtils.bytesToHex(applicationKey.getKey(), false) );

//            unicastAddressView.setText(getString(R.string.ble_hex_format,
//                    String.format(Locale.US, "%04X", meshNetworkLiveData.getMeshNetwork().getUnicastAddress())));
	});

	//监听设备识别
	provisioningViewModel.getUnprovisionedMeshNode().observe(this, meshNode -> {
		Log.e(TAG, "getUnprovisionedMeshNode:meshNode=" + (meshNode == null));
		if (meshNode != null) {
			final ProvisioningCapabilities capabilities = meshNode.getProvisioningCapabilities();
			Log.e(TAG, "getUnprovisionedMeshNode:capabilities=" + (capabilities == null));
			if (capabilities != null) {
				final MeshNetwork network = provisioningViewModel.getNetworkLiveData().getMeshNetwork();
				if (network != null) {
					try {
						final int elementCount = capabilities.getNumberOfElements();
						final Provisioner provisioner = network.getSelectedProvisioner();
						final int unicast = network.nextAvailableUnicastAddress(elementCount, provisioner);
						network.assignUnicastAddress(unicast);
					} catch (IllegalArgumentException ex) {
						ToastUtil.showToast(mContext, ex.getMessage());
					}
				}
			}
		}
	});
}

2.3 选择模型和节点

//选择节点
public ProvisionedMeshNode setSelectNode(String controlAddress) {
	MeshNetwork network = Consts.sharedViewModel.getNetworkLiveData().getMeshNetwork();
	ProvisionedMeshNode node = network.getNode(Integer.parseInt(controlAddress));
	if (node != null) {
		Consts.sharedViewModel.setSelectedMeshNode(node);
		mElements.clear();
		mElements.addAll(node.getElements().values());
		tag2:
		for (int i = 0; i < mElements.size(); i++) {
			List<MeshModel> models = new ArrayList<>(mElements.get(i).getMeshModels().values());
			tag1:
			for (int j = 0; j < models.size(); j++) {
				if (models.get(j) instanceof VendorModel) {
					Consts.modelConfigurationViewModel.setSelectedElement(mElements.get(i));
					Consts.modelConfigurationViewModel.setSelectedModel(models.get(j));
					break tag2;
				}
			}
		}
	}
	return node;
}

2.4 配置入网

//入网
@SuppressLint("RestrictedApi")
public void provisionClick() {
	final UnprovisionedMeshNode node = provisioningViewModel.getUnprovisionedMeshNode().getValue();
	Log.e(TAG, "isConnected: ((((((((((((( " + (node == null));
	if (node == null) {
		Log.e(TAG, "isConnected: " + provisioningViewModel.getNetworkLiveData().getNodeName());
		provisioningViewModel.getNrfMeshRepository().identifyNode(selectedBluetoothDevice);
		return;
	}
	//配置入网
	if (node.getProvisioningCapabilities() != null) {
		Log.e(TAG, "onCreate: " + (node.getProvisioningCapabilities().getAvailableOOBTypes().size() == 1 &&
				node.getProvisioningCapabilities().getAvailableOOBTypes().get(0) == AuthenticationOOBMethods.NO_OOB_AUTHENTICATION));
		if (node.getProvisioningCapabilities().getAvailableOOBTypes().size() == 1 &&
				node.getProvisioningCapabilities().getAvailableOOBTypes().get(0) == AuthenticationOOBMethods.NO_OOB_AUTHENTICATION) {
			onNoOOBSelected();
		} else {
//               final DialogFragmentSelectOOBType fragmentSelectOOBType = DialogFragmentSelectOOBType.newInstance(meshNode.getProvisioningCapabilities());
//               fragmentSelectOOBType.show(getSupportFragmentManager(), null);
		}
	}
}


@SuppressLint("RestrictedApi")
public void setupProvisionerStateObservers() {
	provisioningViewModel.getProvisioningStatus().observe(this, provisioningStateLiveData -> {
		if (provisioningStateLiveData != null) {
			final ProvisionerProgress provisionerProgress = provisioningStateLiveData.getProvisionerProgress();
			provisioningStateLiveData.getStateList();
			if (provisionerProgress != null) {
				final ProvisionerStates state = provisionerProgress.getState();
				Log.e(TAG, "setupProvisionerStateObservers: state:" + state);
				switch (state) {
					case PROVISIONING_CAPABILITIES:
						Log.e("TAG", "PROVISIONING_CAPABILITIES: " + provisioningViewModel.getNetworkLiveData().getMeshNetwork().getUnicastAddress());
						String address = String.format(Locale.US, "%04X", provisioningViewModel.getNetworkLiveData().getMeshNetwork().getUnicastAddress());
						addressMap.put(selectedBluetoothDevice.getAddress(), address);
						break;
					case PROVISIONING_FAILED://失败
//                        if (getSupportFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_PROVISIONING_FAILED) == null) {
//                            final String statusMessage = ProvisioningFailedState.parseProvisioningFailure(getApplicationContext(), provisionerProgress.getStatusReceived());
//                            DialogFragmentProvisioningFailedError message = DialogFragmentProvisioningFailedError.newInstance(getString(R.string.ble_title_error_provisioning_failed), statusMessage);
//                            message.show(getSupportFragmentManager(), DIALOG_FRAGMENT_PROVISIONING_FAILED);
//                        }
						break;
					case PROVISIONING_AUTHENTICATION_STATIC_OOB_WAITING:
					case PROVISIONING_AUTHENTICATION_OUTPUT_OOB_WAITING:
					case PROVISIONING_AUTHENTICATION_INPUT_OOB_WAITING:
//                        if (getSupportFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_AUTH_INPUT_TAG) == null) {
//                            DialogFragmentAuthenticationInput dialogFragmentAuthenticationInput = DialogFragmentAuthenticationInput.
//                                    newInstance(mViewModel.getUnprovisionedMeshNode().getValue());
//                            dialogFragmentAuthenticationInput.show(getSupportFragmentManager(), DIALOG_FRAGMENT_AUTH_INPUT_TAG);
//                        }
						break;
					case PROVISIONING_AUTHENTICATION_INPUT_ENTERED:
//                        final DialogFragmentAuthenticationInput fragment = (DialogFragmentAuthenticationInput) getSupportFragmentManager().
//                                findFragmentByTag(DIALOG_FRAGMENT_AUTH_INPUT_TAG);
//                        if (fragment != null) {
//                            fragment.dismiss();
//                        }
						break;
					case PROVISIONING_COMPLETE:
					case NETWORK_TRANSMIT_STATUS_RECEIVED:
//                        if (getSupportFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_CONFIGURATION_STATUS) == null) {
//                            DialogFragmentConfigurationComplete fragmentConfigComplete = DialogFragmentConfigurationComplete.
//                                    newInstance(getString(R.string.title_configuration_compete), getString(R.string.configuration_complete_summary));
//                            fragmentConfigComplete.show(getSupportFragmentManager(), DIALOG_FRAGMENT_CONFIGURATION_STATUS);
//                        }
						Log.e(TAG, "setupProvisionerStateObservers: " + "PROVISIONING_COMPLETE");
						handlerProvisioning.sendEmptyMessageDelayed(3, 3000);
						break;
					case PROVISIONER_UNASSIGNED:
						setResultIntent();
						break;
					default:
						break;
				}
			}
		}
	});

}

 public void onNoOOBSelected() {
        final UnprovisionedMeshNode node = provisioningViewModel.getUnprovisionedMeshNode().getValue();
        if (node != null) {
            try {
                node.setNodeName(provisioningViewModel.getNetworkLiveData().getNodeName());
                setupProvisionerStateObservers();
                provisioningViewModel.getMeshManagerApi().startProvisioning(node);
            } catch (IllegalArgumentException ex) {
                ToastUtil.showToast(mContext, ex.getMessage());
            }
        }
    }

2.5 发送消息

/**
 * Send vendor model acknowledged message
 *
 * @param opcode     opcode of the message
 * @param parameters parameters of the message
 */
public void sendVendorModelMessage(final int opcode, final byte[] parameters, final boolean acknowledged) {
	final Element element = Consts.modelConfigurationViewModel.getSelectedElement().getValue();
	if (element != null) {
		final VendorModel model = (VendorModel) Consts.modelConfigurationViewModel.getSelectedModel().getValue();
		if (model != null) {
			final int appKeyIndex = Consts.modelConfigurationViewModel.getMeshManagerApi().getMeshNetwork().getAppKey(0).getKeyIndex();
			// final int appKeyIndex = model.getBoundAppKeyIndexes().get(0);
			final ApplicationKey appKey = Consts.modelConfigurationViewModel.getNetworkLiveData().getMeshNetwork().getAppKey(appKeyIndex);
			final MeshMessage message;
			if (acknowledged) {
				message = new VendorModelMessageAcked(appKey, model.getModelId(), model.getCompanyIdentifier(), opcode, parameters);
				int address = element.getElementAddress();
				if (lightEquipmentGroup != null && lightEquipmentGroup.getConnectMethod() == 0) {
					address = Consts.sharedViewModel.getSelectedGroup().getValue().getAddress();
				}
				sendMessage(address, message);
			} else {
				message = new VendorModelMessageUnacked(appKey, model.getModelId(), model.getCompanyIdentifier(), opcode, parameters);
				int address = element.getElementAddress();
				if (lightEquipmentGroup != null && lightEquipmentGroup.getConnectMethod() == 0) {
					address = Consts.sharedViewModel.getSelectedGroup().getValue().getAddress();
				}
				sendMessage(address, message);
			}
		}
	}
}

protected void sendMessage(final int address, @NonNull final MeshMessage meshMessage) {
	try {
		Log.e(TAG, "sendMessage: " + checkConnectivity());
		if (!checkConnectivity())
			return;
		Consts.modelConfigurationViewModel.getMeshManagerApi().createMeshPdu(address, meshMessage);
	} catch (IllegalArgumentException ex) {
		ToastUtil.showToast(mContext, getString(R.string.ble_title_error));
	}
}

2.6 订阅网络群组

//订阅网络群组
    public void subscribe() {
        final ProvisionedMeshNode meshNode = modelConfigurationViewModel.getSelectedMeshNode().getValue();
        Log.e(TAG, "meshNodeIsnull: " + (meshNode == null));
        if (meshNode != null) {
            final Element element = modelConfigurationViewModel.getSelectedElement().getValue();
            Log.e(TAG, "elementIsnull: " + (element == null));
            if (element != null) {
                final int elementAddress = element.getElementAddress();
                final MeshModel model = modelConfigurationViewModel.getSelectedModel().getValue();
                Log.e(TAG, "modelIsnull: " + (model == null));
                if (model != null) {
                    final int modelIdentifier = model.getModelId();
                    final MeshMessage configModelSubscriptionAdd;
                    Log.e(TAG, "group.getAddressLabel(): " + (group.getAddressLabel() == null));
                    if (group.getAddressLabel() == null) {
                        configModelSubscriptionAdd = new ConfigModelSubscriptionAdd(elementAddress, group.getAddress(), modelIdentifier);
                    } else {
                        configModelSubscriptionAdd = new ConfigModelSubscriptionVirtualAddressAdd(elementAddress, group.getAddressLabel(), modelIdentifier);
                    }

                    sendMessage(meshNode.getUnicastAddress(), configModelSubscriptionAdd);

                    handlerCheckIsConnectIndex=addIndex;
                    handlerCheckIsConnect.removeCallbacksAndMessages(0);
                    handlerCheckIsConnect.sendEmptyMessageDelayed(1,3000);
                }
            }
        }
    }

 2.7 接收消息

public void onMeshMessageReceived(final int src, @NonNull final MeshMessage meshMessage) {
        final ProvisionedMeshNode node = mMeshNetwork.getNode(src);
        if (node != null)
            if (meshMessage instanceof ProxyConfigFilterStatus) {
                mProvisionedMeshNode = node;
                setSelectedMeshNode(node);
                final ProxyConfigFilterStatus status = (ProxyConfigFilterStatus) meshMessage;
                final int unicastAddress = status.getSrc();
                Log.v(TAG, "Proxy configuration source: " + MeshAddress.formatAddress(status.getSrc(), false));
                mConnectedProxyAddress.postValue(unicastAddress);
                mMeshMessageLiveData.postValue(status);
            } else if (meshMessage instanceof ConfigCompositionDataStatus) {
                final ConfigCompositionDataStatus status = (ConfigCompositionDataStatus) meshMessage;
                if (mSetupProvisionedNode) {
                    mIsCompositionDataReceived = true;
                    mProvisionedMeshNodeLiveData.postValue(node);
                    mConnectedProxyAddress.postValue(node.getUnicastAddress());
                    mProvisioningStateLiveData.onMeshNodeStateUpdated(ProvisionerStates.COMPOSITION_DATA_STATUS_RECEIVED);
                    mHandler.postDelayed(() -> {
                        Log.e(TAG, "onMeshMessageReceived: 500" );
                        final ConfigDefaultTtlGet configDefaultTtlGet = new ConfigDefaultTtlGet();
                        Log.e(TAG, "onMeshMessageReceived: 3" );
                        mMeshManagerApi.createMeshPdu(node.getUnicastAddress(), configDefaultTtlGet);
                    //}, 500);
                    }, 0);
                } else {
                    updateNode(node);
                }
            } else if (meshMessage instanceof ConfigDefaultTtlStatus) {
                final ConfigDefaultTtlStatus status = (ConfigDefaultTtlStatus) meshMessage;
                if (mSetupProvisionedNode) {
                    mIsDefaultTtlReceived = true;
                    mProvisionedMeshNodeLiveData.postValue(node);
                    mProvisioningStateLiveData.onMeshNodeStateUpdated(ProvisionerStates.DEFAULT_TTL_STATUS_RECEIVED);
                    mHandler.postDelayed(() -> {
                        Log.e(TAG, "onMeshMessageReceived: 1500" );
                        final ApplicationKey appKey = mMeshNetworkLiveData.getSelectedAppKey();
                        @SuppressLint("RestrictedApi") final int index = node.getAddedNetKeys().get(0).getIndex();
                        final NetworkKey networkKey = mMeshNetwork.getNetKeys().get(index);
                        final ConfigAppKeyAdd configAppKeyAdd = new ConfigAppKeyAdd(networkKey, appKey);
                        Log.e(TAG, "onMeshMessageReceived: 2" );
                        mMeshManagerApi.createMeshPdu(node.getUnicastAddress(), configAppKeyAdd);
                    //}, 1500);
                    }, 0);
                } else {
                    updateNode(node);
                    mMeshMessageLiveData.postValue(status);
                }
            } else if (meshMessage instanceof ConfigAppKeyStatus) {
                final ConfigAppKeyStatus status = (ConfigAppKeyStatus) meshMessage;
                if (mSetupProvisionedNode) {
                    if (status.isSuccessful()) {
                        mIsAppKeyAddCompleted = true;
                        mProvisionedMeshNodeLiveData.postValue(node);
                        mProvisioningStateLiveData.onMeshNodeStateUpdated(ProvisionerStates.APP_KEY_STATUS_RECEIVED);
                        mHandler.postDelayed(() -> {
                            final ConfigNetworkTransmitSet networkTransmitSet = new ConfigNetworkTransmitSet(2, 1);
                            Log.e(TAG, "onMeshMessageReceived: 1" );
                            mMeshManagerApi.createMeshPdu(node.getUnicastAddress(), networkTransmitSet);
                        // }, 1500);
                        }, 0);
                    }
                } else {
                    updateNode(node);
                    mMeshMessageLiveData.postValue(status);
                }
            } else if (meshMessage instanceof ConfigNetworkTransmitStatus) {
                if (mSetupProvisionedNode) {
                    mSetupProvisionedNode = false;
                    mIsNetworkRetransmitSetCompleted = true;
                    mProvisioningStateLiveData.onMeshNodeStateUpdated(ProvisionerStates.NETWORK_TRANSMIT_STATUS_RECEIVED);
                } else {
                    updateNode(node);
                    final ConfigNetworkTransmitStatus status = (ConfigNetworkTransmitStatus) meshMessage;
                    mMeshMessageLiveData.postValue(status);
                }
            } else if (meshMessage instanceof ConfigModelAppStatus) {
                if (updateNode(node)) {
                    final ConfigModelAppStatus status = (ConfigModelAppStatus) meshMessage;
                    final Element element = node.getElements().get(status.getElementAddress());
                    if (node.getElements().containsKey(status.getElementAddress())) {
                        mSelectedElement.postValue(element);
                        final MeshModel model = element.getMeshModels().get(status.getModelIdentifier());
                        mSelectedModel.postValue(model);
                    }
                }

            } else if (meshMessage instanceof ConfigModelPublicationStatus) {

                if (updateNode(node)) {
                    final ConfigModelPublicationStatus status = (ConfigModelPublicationStatus) meshMessage;
                    if (node.getElements().containsKey(status.getElementAddress())) {
                        final Element element = node.getElements().get(status.getElementAddress());
                        mSelectedElement.postValue(element);
                        final MeshModel model = element.getMeshModels().get(status.getModelIdentifier());
                        Log.e(TAG, "onMeshMessageReceived: ****************************" );
                        mSelectedModel.postValue(model);
                    }
                }

            } else if (meshMessage instanceof ConfigModelSubscriptionStatus) {

                if (updateNode(node)) {
                    final ConfigModelSubscriptionStatus status = (ConfigModelSubscriptionStatus) meshMessage;
                    if (node.getElements().containsKey(status.getElementAddress())) {
                        final Element element = node.getElements().get(status.getElementAddress());
                        mSelectedElement.postValue(element);
                        final MeshModel model = element.getMeshModels().get(status.getModelIdentifier());
                        mSelectedModel.postValue(model);
                    }
                }

            } else if (meshMessage instanceof ConfigNodeResetStatus) {
                mBleMeshManager.setClearCacheRequired();
                final ConfigNodeResetStatus status = (ConfigNodeResetStatus) meshMessage;
                mExtendedMeshNode.postValue(null);
                Log.e(TAG, "onMeshMessageReceived: 2" );
                loadNodes();
                mMeshMessageLiveData.postValue(status);

            } else if (meshMessage instanceof ConfigRelayStatus) {
                if (updateNode(node)) {
                    final ConfigRelayStatus status = (ConfigRelayStatus) meshMessage;
                    mMeshMessageLiveData.postValue(status);
                }

            } else if (meshMessage instanceof ConfigProxyStatus) {
                if (updateNode(node)) {
                    final ConfigProxyStatus status = (ConfigProxyStatus) meshMessage;
                    mMeshMessageLiveData.postValue(status);
                }

            } else if (meshMessage instanceof GenericOnOffStatus) {
                if (updateNode(node)) {
                    final GenericOnOffStatus status = (GenericOnOffStatus) meshMessage;
                    if (node.getElements().containsKey(status.getSrcAddress())) {
                        final Element element = node.getElements().get(status.getSrcAddress());
                        mSelectedElement.postValue(element);
                        final MeshModel model = element.getMeshModels().get((int) SigModelParser.GENERIC_ON_OFF_SERVER);
                        mSelectedModel.postValue(model);
                    }
                }
            } else if (meshMessage instanceof GenericLevelStatus) {

                if (updateNode(node)) {
                    final GenericLevelStatus status = (GenericLevelStatus) meshMessage;
                    if (node.getElements().containsKey(status.getSrcAddress())) {
                        final Element element = node.getElements().get(status.getSrcAddress());
                        mSelectedElement.postValue(element);
                        final MeshModel model = element.getMeshModels().get((int) SigModelParser.GENERIC_LEVEL_SERVER);
                        mSelectedModel.postValue(model);
                    }
                }

            } else if (meshMessage instanceof VendorModelMessageStatus) {

                if (updateNode(node)) {
                    final VendorModelMessageStatus status = (VendorModelMessageStatus) meshMessage;
                    if (node.getElements().containsKey(status.getSrcAddress())) {
                        final Element element = node.getElements().get(status.getSrcAddress());
                        mSelectedElement.postValue(element);
                        final MeshModel model = element.getMeshModels().get(status.getModelIdentifier());
                        mSelectedModel.postValue(model);
                    }
                }
            }

        if (mMeshMessageLiveData.hasActiveObservers()) {
            mMeshMessageLiveData.postValue(meshMessage);
        }

        //Refresh mesh network live data
        mMeshNetworkLiveData.refresh(mMeshManagerApi.getMeshNetwork());
    }

2.9 字节数据的转换,8位二进制一个字节

public static String hexStringFormatNormal1(CmdNormal cmdNormal) {
        if (cmdNormal == null) {
            return "";
        }
        /**
         * 功能码8位二进制组成功能byte
         * Bit[0]  :  0-不需要从机返回信息 /  1-需要从机返回信息
         * Bit[1]  :  0-发送 /  1-返回
         * Bit[3]  :  0-快捷指令 /  1-常规指令
         * Bit[4:3] :  0-蓝牙灯具 /  1-2.4G灯具 /  2-DMX灯具
         * Bit[6:5] :  未使用,保持0
         * Bit[7]  :  0-独立一帧 / 1-多帧数据
         */
        StringBuffer stringBufferFunction = new StringBuffer();
        CmdFunction cmdFunction = cmdNormal.getCmdFunction();
        stringBufferFunction.append(cmdFunction.getIsMultiFrame());//7位(0-独立一帧 / 1-多帧数据)
        stringBufferFunction.append("0");//6位保持0
        stringBufferFunction.append(cmdFunction.getIsSetting());//5 0-查询 / 1-设置
        //stringBufferFunction.append("0");//3位保持0
        stringBufferFunction.append(OrderUtils.numToBinary(cmdFunction.getIsDeviceType(),2));//3,4位(0-蓝牙灯具 /  1-2.4G灯具 /  2-DMX灯具)
        stringBufferFunction.append(cmdFunction.getIsFunctionNormal());//2位 (0-快捷指令 /  1-常规指令)
        stringBufferFunction.append(cmdFunction.getIsFunctionBack());//1位(0-发送 /  1-返回)
        stringBufferFunction.append(cmdFunction.getIsMachineBack());//0位(0-不需要从机返回信息 /  1-需要从机返回信息)


        //功能码二进制转10进制
        int functionTen = Integer.parseInt(stringBufferFunction.toString(), 2);

        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(bytesToHexString(cmdNormal.getRollCode()));//滚码
        stringBuffer.append(bytesToHexString(functionTen));//功能码

        //地址码
        int address=cmdNormal.getAddress();
        stringBuffer.append(bytesToHexString(address>> 8 & 0xff));
        stringBuffer.append(bytesToHexString(address& 0xff));

        //当前帧
        stringBuffer.append(bytesToHexString(cmdNormal.getCurrentFrame()));
        //总帧
        stringBuffer.append(bytesToHexString(cmdNormal.getTotalFrame()));
        //数据模式
        stringBuffer.append(bytesToHexString(cmdNormal.getModeType()));

        CmdNormal.AllDataMode allDataMode=cmdNormal.getAllDataMode();
        for(CmdCode cmdCode:allDataMode.getCmdCodeList()){
            if(cmdCode.getLenth()==1){
                stringBuffer.append(bytesToHexString(cmdCode.getValue()));
            }else if(cmdCode.getLenth()==2){
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 8 & 0xff));//高8位
                stringBuffer.append(bytesToHexString(cmdCode.getValue() & 0xff));//低8位
            }else if(cmdCode.getLenth()==4){
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 24 & 0xff));//高24位
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 16 & 0xff));//高16位
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 8 & 0xff));//高8位
                stringBuffer.append(bytesToHexString(cmdCode.getValue() & 0xff));//低8位
            }else if(cmdCode.getLenth()==6){
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 40 & 0xff));//高40位
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 32 & 0xff));//高32位
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 24 & 0xff));//高24位
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 16 & 0xff));//高16位
                stringBuffer.append(bytesToHexString(cmdCode.getValue()>> 8 & 0xff));//高8位
                stringBuffer.append(bytesToHexString(cmdCode.getValue() & 0xff));//低8位
            }
        }
        return stringBuffer.toString();
    }

 

三,总结:

3.1 整个Mesh通信拓扑的实现还是比较复杂的,所以开源的可能不好找,我也是基于收费厂家的一套Mesh方案实现的组网的步骤,有兴趣的可以了解下组网的概念和组网的流程。

3.2 组网步骤总结:

第一步肯定还是扫描设备,毕竟这是蓝牙最基本功能

Mesh节点在网络内发送数据不会像普通BLE广播需要等一个固定的广播间隔,而是延迟一小段随机时间后发送,所以为了数据不丢失,节点会启用100%占空比来扫描广播信道,扫描窗口时间=扫描间隔

第二步检测key连接设备

mesh对传输的数据进行分层次加密,网络层(Network Layer)数据通过网络密钥(Network Key)加密;应用密钥(App Key)用于加密接入层(Access Layer)数据;配置模型(Configuration Model)的数据则采用设备密钥(Device Key)进行加密

 

第三步选择节点

mesh里面还给每个节点有一些额外的四种可选的特性(Features)。分别是中继Relay,代理Proxy,朋友Friend和 低功耗Low Power features。节点可以在某个时间点选择不支持或者支持多个Feature。

中继(Relay)支持中继的节点,可以帮忙转发收到的消息。因为有了Relay,Mesh网络就可以实现多跳(Hops)。

低功耗和朋友(Low Power Nodes and Friend Nodes), 这是搭配来用的。我们先说Low power节点,类似于对功耗有要求的设备,例如温度传感器。这种类型的设备为了节约功耗,很大的时间都是在休眠的。也就是意味着他们收不到网络中发过来的消息。Friend节点能帮LP节点暂存消息。当LP节点需要的时候,可以发消息给Friend节点, 问问有没有“waiting message”。如果有,就会一条条的发给LP节点。简而言之,Friend节点就像是门卫的张大爷,你(Low power node)想起来的时候去门卫拿你要的信就好了。这种方式和zigbee里面的enddevice向父节点拿数据的方式类似

第四步配置入网

所谓配网就是将未配网的设备变为配网的节点,一般需要一个配网器与末配网设备进行配网交互、验证然后通过后将一些密钥交给对方的一个过程。
一般过程有5个阶段:
1. 信标(Beaconing)阶段
2.邀请 (Invitation)阶段
3. 交换公钥 (Exchange Public Keys)阶段
4. 身份认证 CAuthentication)阶段
5. 分发配网数据 (Distribution Of Provisioning Data)阶段

第五步分配地址

单播地址:分配给节点中的元素地址,地址范围0x0001~0x7FFF,
未分配地址:即无效地址,固定为0x0000,地址的初始值,常用于屏蔽一个设备
组播地址:用于表示一个或多个节点的多个元素,地址范围0xC000~0xFFFF,其中包含256个固定组播地址
虚拟地址:用于表示一个或多个节点的多个元素,每一个虚拟地址逻辑上对应一个128-bit的Label UUID,通过对该Label UUID作哈希运算得出虚拟地址的低14位数值,虚拟地址的范围为0x8000~0xBFFF

第六步选择模型

模型(Model)定义了节点基本功能的最小单位模型,包含实现这个功能所必需的状态和操作状态的消息及其他一些行为 

在蓝牙mesh模型里,消息通信基于客户端-服务器的架构,对外提供状态访问接口的叫做服务器(Server),而访问服务器状态的叫做客户端,模型分为三种

服务器模型:服务器模型包含了一个或多个元素上的一种或多种状态,比如灯泡上包含有通用开关服务器模型(Generic OfOff Server)和灯泡亮度服务器模型(Light Lightneww Server)
客户端模型:客户端模型定义了一系列的消息,用于客户端去请求、设置服务端的状态,比如开关中含有通用开关客户端模型(Generic OnOff Client)以及灯亮度客户端模型(Light Lightness Client),客户端模型不含有状态
控制模型:控制模型可以包含一个或多个客户端模型,用来和其他节点的服务端模型通信;也可以包含一个或多个服务端模型,用于响应其他节点客户端模型发来的消息
蓝牙技术联盟定义的模型被称为标准模型(SIG Adopted Model),16bit标识,目前SIG定义好的模型包括Generic、Sensors、Time and Scenes、Lighting;由厂商定义的模型称为厂商模型(Vendor Model),32bit标识。

第七步发布和订阅

在蓝牙mesh里面发消息的动作我们叫做发布(Publish)。光从字面意思理解大家基本上就能看懂了。我想告诉别人什么事情发生或者做什么事情就叫做发布。谁对某些消息感兴趣就可以订阅这些内容。节点发布消息到单播地址,组播地址或者虚拟地址。节点有兴趣接收这些数据的可以订阅这些地址。

第八步发送消息

蓝牙Mesh采用了消息缓存队列和TTL的优化方案来避免消息的无限制转发。

消息缓存 Message cache:设备都会缓存收到消息的关键信息,以确定是否已经转发过此消息,如果是就忽略此消息。Message cache至少需要能缓存两条消息
Time to Live(TTL): 每个消息都会包含一个Time to Live(TTL)的值,来限制中继的次数,最大可以中继126次。消息每转发一次TTL的值就减1,TTL值为1就不再转发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/458960.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

物联仓储系统ZigBee组网原理

在嵌入式项目物联仓储系统中&#xff0c;使用cortexM0模拟仓库&#xff0c;cortex-A9模拟服务器&#xff0c;两块开发板之间使用ZigBee技术实现数据接收和发送&#xff0c;本文就介绍一下ZigBee组网的原理和相关步骤。 1.组网概述 组建一个完整的zigbee网状网络包括两个步骤&am…

VSCode连接远程服务器调试代码详细流程

文章目录 1.远程连接服务器2. 打开项目文件目录3. 配置调试环境 在研究人工智能项目时&#xff0c;很多时候本地机器性能不够&#xff0c;只能把代码拉倒服务器上&#xff0c;然后利用服务器资源来运行代码。遇到问题时需要调试&#xff0c;本文详细介绍利用VScode来调试远程服…

简述docker镜像制作:阿里云私服使用说明

阿里云私服使用说明 使用阿里云容器镜像服务 在使用docker时&#xff0c;为了方便管理docker镜像和版本迭代&#xff0c;咱们推荐有一个镜像服务器。这里我们比较推荐使用服务商提供的容器镜像服务&#xff0c;一来不用自己搭建私服&#xff0c;节省维护成本&#xff0c;二来带…

【算法学习】—n皇后问题(回溯法)

【算法学习】—n皇后问题(回溯法) 1. 什么是回溯法&#xff1f; 相信"迷宫"是许多人儿时的回忆&#xff0c;大家小时候一定都玩过迷宫游戏。我们从不用别人教&#xff0c;都知道走迷宫的策略是&#xff1a; 当遇到一个岔路口&#xff0c;会有以下两种情况&#xf…

vue组件之间的数据共享

1.组件之间的关系 在项目开发中&#xff0c;组件之间的最常见的关系分为如下两种: ① 父子关系 ② 兄弟关系 1.父向子传值---使用自定义属性 父组件向子组件共享数据需要使用自定义属性。示例代码如下: 父组件&#xff1a; <Son :msg"message" :user"…

使用AI优化慢SQL,开发秒变DBA

“AI不会替代他们&#xff0c;但善用AI的人会” 慢 SQL 经常会让应用程序响应变慢&#xff0c;轻者影响用户体验&#xff0c;严重的时候可能会导致服务不可用。如果&#xff0c;每次遇到慢 SQL 都求助于 DBA&#xff0c;一方面效率很低&#xff0c;另一方面也会很没面子。所以…

PowerShell批量修改、替换大量文件的文件名

本文介绍基于PowerShell语言&#xff0c;对文件夹中全部文件的名称加以批量替换、修改的方法。 在之前的文章基于Python一次性批量修改多个文件的文件名&#xff08;https://blog.csdn.net/zhebushibiaoshifu/article/details/115869725&#xff09;中&#xff0c;我们介绍了基…

为什么重写equals时必须重写hashCode()

不重写equals和不重写 hashCode()之前&#xff1a;equals()比较的是对象的内存地址&#xff0c;hashCode()比较的其实也是内存地址(内存地址输入到哈希函数中得到的整数) 重写了之后&#xff0c;equals()比较的是对象的内容值&#xff0c;如果hashCode()不重写&#xff0c;还是…

log是什么文件可以删除吗?log文件被删怎么恢复?

在工作和生活中&#xff0c;我们难免会遇到因操作失误、软件崩溃或病毒攻击等原因导致重要文件被误删的情况&#xff0c;这其中包括log文件。如果您不幸遇到log文件被误删的情况&#xff0c;不要过于担心&#xff0c;本文将为您介绍几种方法&#xff0c;帮助您找回被误删除的lo…

【hello Linux】动静态库

目录 1. 了解动静态库 1. ldd 命令的使用 2. C语言库与C语言库 3. 库的分类 4. 库的命名 5. 库的内容 2. 静态库的制作和使用 1. 静态库的制作 2. 静态库的使用 3.动态库的制作和使用 1. 动态库的制作 2. 动态库的使用 Linux&#x1f337; 1. 了解动静态库 1. ldd 命令的使用 …

JavaScript 鼠标事件监听触发时机触发顺序

有时间整理下鼠标的监听事件&#xff0c;目前汇总到的鼠标监听事件以下10个&#xff1a; 目录 1. click 2. dblclick 3.contextmenu 4.mousedown 5.mouseup 6.mouseenter 7.mouseleave 8.mouseover 9.mouseout 10.mousemove 先说下触发时机和作用键&#xff08;左键…

九州云出席全球分布式云大会,基于5G MEC的车路协同创新引关注

4月20日&#xff0c;以“云智相生”为主题的2023全球分布式云大会北京站在北京成功召开。本次大会作为AIGC数智中国科技周的重要组成部分&#xff0c;聚焦分布式云和人工智能&#xff0c;探讨分布式云如何更好地支持人工智能发展&#xff0c;打造MaaS&#xff08;模型即服务&am…

2023年4月中旬值得关注的几个AI模型:Dollly2、MiniGPT-4、LLaVA、DINOv2

AI模型的发展速度令人惊讶&#xff0c;几乎每天都会有新的模型发布。而2023年4月中旬也有很多新的模型发布&#xff0c;我们挑出几个重点给大家介绍一下。 Dolly-v2 MiniGPT-4 LLaVA DINOv2 Dolly-v2 Dolly是EleutherAI开源的一系列大语言模型&#xff0c;EleutherAI认为…

vue的diff算法原理

diff 概念diff比较流程头头尾尾头尾尾头比对查找过程 与vue3的区别 diff 概念 vue基于虚拟DOM做更新&#xff0c;diff的核心就是比较两个虚拟节点的差异。 vue的diff算法是平级比较&#xff0c;不考虑跨级比较的情况。内部采用深度递归 双指针的方式进行比较 diff比较流程 先…

文本挖掘 实践笔记

文本挖掘流程:(How) 文本预处理->特征提取->文本分析->可视化展示 文本预处理:包括文本清洗、分词、词性标注等 特征提取&#xff1a;将文本转化为词频、TF-IDF、embedding向量等 文本分析:利用统计学或机器学习的知识,做聚类、情感识别等 可视化展示:帮助人们更好…

Java版本电子招标采购系统源码:营造全面规范安全的电子招投标环境,促进招投标市场健康可持续发展

营造全面规范安全的电子招投标环境&#xff0c;促进招投标市场健康可持续发展 传统采购模式面临的挑战 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标…

Pandas技术重点来了

Pandas库建立在NumPy之上&#xff0c;并为Python编程语言提供了易于使用的数据结构和数据分析工具。 1.安装及调用 pip install pandasimport pandas as pd 一种能够保存任何数据类型的一维标记数组 >>> s pd.Series([3, -5, 7, 4], index[a, b, c, d]) 一种具有潜…

第八届cccc团体程序设计天梯赛——个人参赛总结——无代码纯粹的参赛总结

第八届cccc团体程序设计天梯赛——个人参赛总结——无代码纯粹的参赛总结 目录 第八届cccc团体程序设计天梯赛——个人参赛总结——无代码纯粹的参赛总结引言~介绍一下cccc天梯赛&#xff08;选读&#xff09;开篇介绍&#xff08;以下是个人经历部分的分享&#xff09;赛前准备…

其他品牌的触控笔能用在ipad上?性价比高的触控笔合集

随着平板电脑在学校、办公室等领域的广泛应用&#xff0c;越来越多的人需要一支出色的电容笔。虽然苹果原装的电容笔很给力&#xff0c;但是其的价格实在是太贵了&#xff0c;仅仅把它用于在纸上写写字&#xff0c;实在是太可惜了。所以&#xff0c;哪个电容笔更便宜&#xff1…

密歇根大学Python系列之一:零基础 Python 入门

密歇根大学计算机专业注重将计算机科学理论与实践相结合&#xff0c;旨在帮助学生全面掌握计算机科学的基础理论和实践技能&#xff1a; 1.计算机程序设计&#xff1a;编程技能和常见编程语言&#xff0c;如C和Java和Python等&#xff1b; 2.数据结构和算法&#xff1a;数据结…