Zigbee +PC上位机 无线控制二维云台开发笔记

news2024/9/25 7:26:02

今日尝试开发一款简单好学的PC上位机无线控制二维云台的小试验品:

主要开发环境与工具介绍:

 单片机 STM32F103C8T6 使用标准库函数编程

 Visual Studio 2022软件C# Winform 开发 上位机控制软件

 DL_20 无线串口模块 + USB-TTL 模块 实现无线通信功能

文章提供完整代码解释、设计点解释、测试效果图、完整工程下载

目录

主要用到的知识如下:

C# Winform上位机的编程:

窗体设计:

Form1初始化:

打开串口 控件函数:

串口接收 控件函数:

串口发送 控件函数:

头部/底部开始移动 控件函数:

一键归位 控件函数:

测试连接 控件函数:

创建日志委托 函数:

清除日志区 控件函数:

注意事项:

C# Winform 整体测试工程下载:

STM32F10xx 单片机的编程:

OLED的驱动显示:

PWM控制舵机运动:

初始化TIM3为舵机控制定时器:

设置TIM3占空比控制舵机运转:

串口接收与串口中断服务函数的编写:

STM32F103C8T6测试工程下载:

测试视频与图片:


主要用到的知识如下:

DL_20无线串口模块_dl20无线串口模块-CSDN博客

C#学习笔记10:winform上位机与西门子PLC网口通信_中篇_winform的窗口操作设计、日志的添加使用_c#网口通信界面-CSDN博客

C# Winform上位机的编程:

窗体设计:

主要用到的控件有Listview、imaginelist、button、checkbox、combobox、label 、serialport

Form1初始化:

       //创建这个窗体的addlog ,需要绑定一个实际方法
       private AddLog myaddlog;
       bool Form1_FClosing = false;//用于防止二次Form1_FormClosing()事件发生的
       string formattedLogMessage; //用于临时拼接字符串
       bool OPEN_SERIAL_flag = false;//打开串口标志 false:未打开
       int angle;                  //角度

       public Form1()
       {
           InitializeComponent();
           this.Load += Form1_Load;
           myaddlog = this.AddLog;//绑定方法
           serialPort1.Encoding = Encoding.GetEncoding("GB2312");     //串口接收编码
           Control.CheckForIllegalCrossThreadCalls = false;
       }

       private void Form1_Load(object sender, EventArgs e)
       {
           设置第一列的宽度=整个宽度 减去 第0页宽度
           lstInfo.Columns[1].Width = lstInfo.ClientSize.Width - lstInfo.Columns[0].Width;

           for (int i = 1; i < 10; i++)//初始化串口 号下拉框内容
           {
               comboBox4.Items.Add("COM" + i.ToString()); //添加串口
           }

           for (int H = 0; H < 5; H++)//初始化串口 波特率下拉框内容
           {
               switch (H)
               {
                   case 0: comboBox5.Items.Add("2400"); break;
                   case 1: comboBox5.Items.Add("4800"); break;
                   case 2: comboBox5.Items.Add("9600"); break;
                   case 3: comboBox5.Items.Add("115200"); break;
               }
           }

           //停止位 下拉框内容
           for (int j = 0; j < 3; j++)
           {
               switch (j)
               {
                   case 0: comboBox7.Items.Add("1"); break;
                   case 1: comboBox7.Items.Add("1.5"); break;
                   case 2: comboBox7.Items.Add("2"); break;
               }
           }

           comboBox4.Text = "COM1";//端口下拉框初始值
           comboBox5.Text = "9600";//波特率下拉框初始值
           comboBox7.Text = "1";//停止位
           comboBox6.Text = "8";//数据位

           serialPort1.Close();   //关闭串行端口连接
       }

打开串口 控件函数:


        //打开/关闭串口
        private void button6_Click(object sender, EventArgs e)
        {
            if(OPEN_SERIAL_flag==false)
            {
                try
                {
                    serialPort1.PortName = comboBox4.Text;//设置端口号
                    serialPort1.BaudRate = Convert.ToInt32(comboBox5.Text);//设置端口波特率
                    serialPort1.StopBits = (StopBits)Convert.ToInt32(comboBox7.Text);//设置停止位
                    serialPort1.DataBits = Convert.ToInt32(comboBox6.Text);//设置数据位
                    serialPort1.ReceivedBytesThreshold = 1;
                    serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
                    serialPort1.Open();                   //打开串口
                    OPEN_SERIAL_flag = true;   //标记打开了串口
                    myaddlog(0, "当前串口有设备连接,串口已成功打开");
                    button6.Text = "关闭串口";
                }
                catch
                {
                    myaddlog(1, "错误警告: 端口无设备连接");
                    button6.Text = "打开串口";
                }

            }
            else if(OPEN_SERIAL_flag == true)
            {
                try
                {
                    serialPort1.Close(); //关闭串口        
                    myaddlog(0, "已关闭串口 ");
                    OPEN_SERIAL_flag = false;
                    button6.Text = "打开串口";
                }
                catch { }
            } 
        }

串口接收 控件函数:

用到的全局变量:

        string formattedLogMessage; //用于临时拼接字符串

        //串口接收
        //一个接收数据事件获取串口发送来的数据
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            //处理事件这块可以加上延时确保不定数的数据可以全部收到缓冲后,才去读缓冲内容--单位: 毫秒
            Thread.Sleep(50);
            //如果16进制转换没被勾选
            if (!checkBox1.Checked)
            {
                myaddlog(0, serialPort1.ReadExisting());
                myaddlog(0, "串口消息接收回传:");
            }
            //如果16进制转换被勾选了
            else
            {
                try
                {
                    //定义缓冲区数组大小为串口缓冲区数据的字节数
                    //因为串口事件触发时有可能收到不止一个字节
                    byte[] data = new byte[serialPort1.BytesToRead];
                    serialPort1.Read(data, 0, data.Length);
                  
                    foreach (byte Member in data)  //遍历用法
                    {
                        string str = Convert.ToString(Member, 16).ToUpper();
                        formattedLogMessage = string.Format("0x" + (str.Length == 1 ? "0" + str : str) + " ");
                        myaddlog(0, formattedLogMessage);
                    }
                    myaddlog(0, "串口消息接收回传:");
                }
                catch { }
            }
        }

串口发送 控件函数:


        //串口测试发送:
        private void button7_Click(object sender, EventArgs e)
        {
            byte[] Data = new byte[1];                                                         //单字节发数据     
            if (serialPort1.IsOpen)
            {
                if (textBox1.Text != "")
                {
                    //如果不是16进制发送,就直接string形式发送
                    if (!checkBox2.Checked)
                    {
                        try
                        {
                            serialPort1.Write(textBox1.Text);
                            myaddlog(0, "单条发送成功");
                            //serialPort1.WriteLine();                             //字符串写入
                        }
                        catch
                        {
                            myaddlog(1, "串口数据写入错误");
                        }
                    }
                    else                                                                    //数据模式
                    {
                        try                                                                 //如果此时用户输入字符串中含有非法字符(字母,汉字,符号等等,try,catch块可以捕捉并提示)
                        {
                            for (int i = 0; i < (textBox1.Text.Length - textBox1.Text.Length % 2) / 2; i++)//转换偶数个
                            {
                                Data[0] = Convert.ToByte(textBox1.Text.Substring(i * 2, 2), 16);           //转换
                                serialPort1.Write(Data, 0, 1);
                            }
                            if (textBox1.Text.Length % 2 != 0)
                            {
                                //单独处理最后一个字符
                                Data[0] = Convert.ToByte(textBox1.Text.Substring(textBox1.Text.Length - 1, 1), 16);
                                serialPort1.Write(Data, 0, 1);//写入
                            }
                            //Data = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);
                            myaddlog(0, "单条发送成功");
                        }
                        catch
                        {
                            myaddlog(1, "数据转换错误,请输入数字。");
                        }
                    }
                }

            }
        }

头部/底部开始移动 控件函数:

       //头部开始移动
       private void button1_Click(object sender, EventArgs e)
       {
           bool success;//用于检查文本框textbox输入规范用

           //先检查串口是否打开
           if (serialPort1.IsOpen == false)
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }

           else
           {
               //尝试转换 textBox2 角度的输入数值,看是否失败
               success = int.TryParse(textBox2.Text.Trim(), out angle);
               if (success == false && serialPort1.IsOpen)
               {
                   myaddlog(1, "无法将框中内容转换,请检查 设定移动角度 的输入。");
               }
               else
               {
                   if (Headleft.Checked && Headright.Checked)
                   {
                       myaddlog(1, "错误!头部移动方向 不可多选!");
                   }
                   else if (Headleft.Checked == false && Headright.Checked == false)
                   {
                       myaddlog(1, "错误!头部移动方向 并未选择!");
                   }
                   else if (Headleft.Checked == true && Headright.Checked == false)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("HL{0}&", textBox2.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送头部 移动方向为左 角度为{0}", textBox2.Text);
                       myaddlog(0, formattedLogMessage);
                   }
                   else if (Headleft.Checked == false && Headright.Checked == true)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("HR{0}&", textBox2.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送头部 移动方向为右 角度为{0}", textBox2.Text);
                       myaddlog(0, formattedLogMessage);
                   }
               }
           }

       }

       //底座开始移动
       private void button2_Click(object sender, EventArgs e)
       {
           bool success;//用于检查文本框textbox输入规范用

           //先检查串口是否打开
           if (serialPort1.IsOpen == false)
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }
           else
           {
               //尝试转换 textBox3 角度的输入数值,看是否失败
               success = int.TryParse(textBox3.Text.Trim(), out angle);
               if (success == false)
               {
                   myaddlog(1, "无法将框中内容转换,请检查 设定移动角度 的输入。");
               }
               else
               {
                   if (Buttomleft.Checked && Buttomright.Checked)
                   {
                       myaddlog(1, "错误!底部移动方向 不可多选!");
                   }
                   else if (Buttomleft.Checked == false && Buttomright.Checked == false)
                   {
                       myaddlog(1, "错误!底部移动方向 并未选择!");
                   }
                   else if (Buttomleft.Checked == true && Buttomright.Checked == false)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("BL{0}&", textBox3.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送底座 移动方向为左 角度为{0}", textBox3.Text);
                       myaddlog(0, formattedLogMessage);
                   }
                   else if (Buttomleft.Checked == false && Buttomright.Checked == true)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("BR{0}&", textBox2.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送底座 移动方向为右 角度为{0}", textBox3.Text);
                       myaddlog(0, formattedLogMessage);
                   }
               }
           }

       }

一键归位 控件函数:

       //一键归位
       private void button3_Click(object sender, EventArgs e)
       {
           if (serialPort1.IsOpen)
           {
               formattedLogMessage = "RS&";
               serialPort1.Write(formattedLogMessage);
               myaddlog(0, "已发送归位测试字符串RS");
           }
           else
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }
       }

测试连接 控件函数:

       //测试连接
       private void button4_Click(object sender, EventArgs e)
       {
           if (serialPort1.IsOpen)
           {
               formattedLogMessage = "TEST&";
               serialPort1.Write(formattedLogMessage);
               myaddlog(0, "已发送测试字符串TEST");
           }
           else
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }
       }

创建日志委托 函数:

 创建委托函数需要放置的位置:

    //info 表示报警级别 ,log 表示报警信息
    public delegate void AddLog(int info, string log);
       //写入日志委托方法
       //创建委托
       private void AddLog(int info, string Log)
       {
           if (!lstInfo.InvokeRequired)
           {
               //创建ListViewItem ,将时间与info放进去
               ListViewItem lst = new ListViewItem("   " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), info);
               lst.SubItems.Add(Log);
               lstInfo.Items.Insert(0, lst);
           }
           else
           {
               Invoke(new Action(() =>
               {
                   ListViewItem lst = new ListViewItem("   " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), info);
                   lst.SubItems.Add(Log);
                   lstInfo.Items.Insert(0, lst);
               }));
           }
       }

清除日志区 控件函数:

        //清除日志区
        private void button8_Click(object sender, EventArgs e)
        {
            lstInfo.Items.Clear();         //清除日志listview 的内容
            MessageBox.Show("已成功清除日志区", "清除接收区");
        }

注意事项:

1、要检查各个控件操作可能出现的错误连接的情况:串口未打开、字符输入非法等,并设置报错日志

2、日志委托写入listview控件,别忘了编辑列

3、

 

C# Winform 整体测试工程下载:

https://download.csdn.net/download/qq_64257614/89368716?spm=1001.2014.3001.5503

STM32F10xx 单片机的编程:

OLED的驱动显示:

有关于OLED的驱动就不多赘述,这里只介绍在哪里刷新了哪些显存,具体配置是有关IIC通信的相关文章贴出如下:

STM32 F103C8T6学习笔记9:0.96寸单色OLED显示屏—自由取模显示—显示汉字与图片_stm32f103c8t6 oled显示文字-CSDN博客

STM32 F103C8T6学习笔记11:RTC实时时钟—OLED手表日历_stm32f103c8t6显示实时时间-CSDN博客

STM32 F103C8T6学习笔记16:1.3寸OLED的驱动显示日历-CSDN博客

PWM控制舵机运动:

初始化TIM3为舵机控制定时器:

        底座舵机:     Signal: PA7
        头部舵机:     Signal: PA6 

设置TIM3占空比控制舵机运转:

这里为了防止舵机运转过快出问题,我使用定时器控制其占空比更新频率不过快,并对占空比输出限幅:然后再主函数调用TIM_SetCompare X();函数来落实占空比的设置:


//通用定时器 定时器1 中断服务函数
void TIM1_UP_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET)
	{	
		if(++t==70)		  //每70ms设置一次舵机占空比
		{
			t=0;
			t1=MIDDLE + t1_receive;
			if(t1>=260) {t1=260;}
			if(t1<150)  {t1=150;}
			
			
			t2=MIDDLE + t2_receive;			
			if(t2>=260) {t2=260;}
			if(t2<150)  {t2=150;}
		}
		
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断
	}
}

串口接收与串口中断服务函数的编写:

这部分的设计有些麻烦,串口接收是一件比较麻烦的事,

这里为了开发迅速,就不自己编写 状态机+结构体 这样比较规范的串口接收方式了,

我选择了简单的 定义接收buff[]数组缓冲区+末尾接收字符检验 的方式进行串口接收校验了,这种方式好编程,但缺点也很多很明显!

定义的诸多变量如下:


int t,t1,t2,t1_receive,t2_receive; //辅助配置占空比
int Receive[20]; //提取 串口接收数组 字符串里的 所有数字 
int temp_Receive;


//定义串口程序需要用到的变量
char USART0_save[20];  //存字符串命令的数组
char USART0_xb=0;			 //帮助数组下标位移
char USART0_flag=0;    //接收完成标志

//定义命令字符串,用于与接收进行比较 ,不可修改
const char str1_order[]="TEST&"; //测试命令
const char str2_order[]="RS&";   //归位命令


//定义响应字符串,用于响应不同的命令
char str1_receive[]="Cotact OVER";
char str2_receive[]="NTM: Hello,STM32F1xx !";
char error_receive[]="ERROR CMD!";

串口中断服务函数:

//串口1中断服务函数 
void USART1_IRQHandler(void)
{
 
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
	{USART_ClearFlag(USART1, USART_FLAG_RXNE);}
		USART0_save[USART0_xb++]=USART_ReceiveData(USART1);
		if(USART0_xb== 20){USART0_xb=0;	}							      //下标最大不超过20
		if(USART0_save[USART0_xb-1]=='&') {USART0_flag=1;}  //命令以&结尾
	
}

串口接收buff[]缓冲处理函数:

void Handle_Uart_Receive(void)
{
		int i;
				if(USART0_flag==1)
				{
					printf("STM32 confirm Receive : %s",USART0_save);            //先重复接受到的字符串
				
				//先判断命令长度,再根据其判断是否为接受到的命令字符串,根据情况发送不同回应
					if(strncmp(USART0_save,str1_order,5)==0) printf("%s",str1_receive);
					else if(strncmp(USART0_save,str2_order,3)==0) 
						{
								 t1_receive=0;t2_receive=0;
								 printf("%s",str2_receive);
							}
						
						//如果是头部舵机转动的命令头	
					if(USART0_save[0]=='H')
							{
								extractDigitsFromStringArray();//提取 USART0_save 接收数组中的数字								
								//循环拼接提取到的每个数字
								for (i = 0; i < USART0_xb -3; i++) 
								{  
											temp_Receive = temp_Receive*10+Receive[i];  
								}
								//判断向左向右
								if(USART0_save[1]=='L'){t1_receive=0-temp_Receive;}
								if(USART0_save[1]=='R'){t1_receive=0+temp_Receive;}
							}
							
								//如果是底部舵机转动的命令头	
							if(USART0_save[0]=='B')
							{
								extractDigitsFromStringArray(); //提取 USART0_save 接收数组中的数字
								//循环拼接提取到的每个数字
								for (i = 0; i < USART0_xb -3; i++) 
								{  
											temp_Receive = temp_Receive*10+Receive[i];  
								}
								//判断向左向右
								if(USART0_save[1]=='L'){t2_receive=0+temp_Receive;}
								if(USART0_save[1]=='R'){t2_receive=0-temp_Receive;}								
							}
							
						}	
						memset(USART0_save,0,sizeof(USART0_save)); //处理完命令别忘了将数组清零,以便接收下个命令
						temp_Receive=0;
						USART0_xb=0;                               //重置数组下标	
						USART0_flag=0;								             //清理标志位
}
	

缓冲处理辅助函数:

这是一些缓冲处理的辅助函数,主要是C语言的基础,对数据类型的处理判断:其中一些基础函数需要添加头文件

#include <ctype.h>   
#include <string.h>  
#include <stdbool.h>  

//从一个数组中提取数字到另一个数组
void extractDigitsFromStringArray(void)
{
	int i=0,j=0;
	for(i=0;i<=sizeof(USART0_save);i++)
	{
		if(isStringNumeric(USART0_save[i])==true)
		{
			Receive[j]=USART0_save[i] - '0';
			j++;
		}
	}
}

// 辅助函数:检查字符是否是数字  
bool isStringNumeric(char str)
{  
    if (str == NULL || str == '\0') 
		{  
        // 空字符或NULL指针,不是数字
        return false;  
    }  
		
    if (!isdigit((unsigned char)str)) 
				{  
            //发现非数字字符,则返回false  
            return false;  
        }  
 
    //字符是数字 
    return true;  
}

STM32F103C8T6测试工程下载:

https://download.csdn.net/download/qq_64257614/89368723?spm=1001.2014.3001.5503

测试视频与图片:

本次小试验品开发前后总共耗时不到俩天,按小时计算的话就少于一天了......

Zigbee +PC上位机 无线控制二维云台开发

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

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

相关文章

tty/pty/console/getty/shell/telnet

tty 终端(termimal)= tty(Teletypewriter, 电传打印机),作用是提供一个命令的输入输出环境,在linux下使用组合键ctrl+alt+T打开的就是终端,可以认为terminal和tty是同义词。 tty泛指所有的终端设置,这些是真实存在的设备。 通过tty命令可以查看当前终端连接的设备。…

水电智能抄表是什么?

1.简述&#xff1a;水电智能抄表的兴起 水电智能抄表系统是现代科学技术和传统公共文化服务相结合的产物&#xff0c;它通过自动化技术性改变了传统的人工抄表方式&#xff0c;大大提高了高效率&#xff0c;降低生产成本&#xff0c;同时也为用户提供了更为贴心的服务。这一新…

5倍收益秘诀:APP广告如何变现?

在这个数字时代&#xff0c;智能手机几乎成了我们生活中不可或缺的一部分。无论是早晨醒来的第一件事&#xff0c;还是睡前的最后一件事&#xff0c;手机都与我们紧密相连。而在这个连接的世界里&#xff0c;APP广告变现成为了一个热门话题&#xff0c;它不仅仅是将每一次点击转…

TCL华星揽获技术创新奖,创新能力与伙伴价值再获肯定

近日&#xff0c;以“拥抱AI共创美好”为主题的2024年联想全球供应商大会在深圳圆满举办&#xff0c;重磅分享联想战略愿景和目标。 TCL华星应邀设置品牌展区&#xff0c;携手机、IT等领域10余款前沿显示产品亮相会场&#xff0c;以先锋显示科技演绎联合共创的多元化场景。联想…

InternLM2-Math-Plus全面升级,全尺寸最强的开源数学模型

总览 数学能力是大语言模型推理水平的重要体现。上海人工智能实验室在推出领先的开源数学模型InternLM2-Math的三个月之后对其进行了升级&#xff0c;发布了全新的 InternLM2-Math-Plus。升级后的 InternLM2-Math-Plus 在预训练和微调数据方面进行了全面的优化&#xff0c;显著…

ES升级--02--kibana安装与启动

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Kibana官网文档https://www.elastic.co/guide/cn/kibana/current/targz.html 1.官网下载https://www.elastic.co/cn/downloads/past-releases#kibana 2.解压软件3.配…

软件系统开发标准流程文档(Word原件)

目的&#xff1a;规范系统开发流程&#xff0c;提高系统开发效率。 立项申请需求分析方案设计方案评审开发调整测试阶段系统培训试运行测试验收投入使用 所有文档过去进主页获取。 软件项目相关全套精华资料包获取方式①&#xff1a;点我获取 获取方式②&#xff1a;本文末个人…

C#【进阶】俄罗斯方块

俄罗斯方块 文章目录 Test1_场景切换相关BeginScene.csBegionOrEndScene.csEndScene.csGame.csGameScene.csISceneUpdate.cs Test2_绘制对象基类和枚举信息DrawObject.csIDraw.csPosition.cs Test3_地图相关Map.cs Test4_坐标信息类BlockInfo.cs Test5_板砖工人类BlockWorker.…

红队技巧:仿冒Windows登录

Metasploit框架&#xff1a;phish_windows_credentials Metasploit带有内置的后期漏洞利用功能&#xff0c;可帮助我们完成任务。由于它是后渗透的模块&#xff0c;因此只需要输入会话即可&#xff1a; use post/windows/gather/phish_windows_credentials set session 1 …

数字信封:保护数据传输的现代安全机制

在数字化时代&#xff0c;数据安全和隐私保护变得尤为重要。随着网络攻击和数据泄露事件的日益增多&#xff0c;传统的加密方法已经难以满足日益增长的安全需求。数字信封&#xff08;Digital Envelope&#xff09;作为一种有效的数据保护技术&#xff0c;提供了一种安全的数据…

视频监控技术前沿探索:智能化趋势与EasyCVR视频汇聚技术应用

在数字化时代的浪潮中&#xff0c;视频监控技术以其独特的优势&#xff0c;正在安防领域书写着新的篇章。它不仅为公共安全部门提供了强大的技术支持&#xff0c;还深入到教育、政府、娱乐、医疗、酒店、运动等多个领域&#xff0c;成为维护社会秩序、保障人民安全的重要工具。…

一套最完整的无人值守配电站房智能辅助监控系统(实用收藏版)

要真正了解无人值守配电室的运行模式&#xff0c;我们必须正确理解“无人值守”。“无人值守”与员工管理真正分离&#xff0c;但借助技术设备&#xff0c;无人值守配电站房智能辅助监控系统通过人机合理协调&#xff0c;确保配电室的正常运行。 通过无人值守配电站房智能辅助…

2024年6月PMP考试考前冲刺攻略

调整心态 考场就像战场一样&#xff0c;不仅是实力的较量&#xff0c;更是心理素质的较量。如果感到过于焦虑&#xff0c;可以通过运动等方式来缓解&#xff0c;也可以多与家人、朋友和老师沟通。只有稳定心态才能发挥出最大的实力&#xff01; 高效学习方法 课本是基础&…

【Sql Server】随机查询一条表记录,并重重温回顾下存储过程的封装和使用

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言随机查询语…

4.共享文件夹的设置

注&#xff1a;设置共享文件夹&#xff1a;首先要先关机 一、点击 编辑虚拟机设置 二、点击 选项 选项卡 三、点击 共享文件夹 四、在本地建立一个共享文件夹后选择路径 五、Linux系统文件夹中的共享文件夹路径 六、在Linux系统中查看共享文件夹 Ubuntushare是共享文件夹&…

protobuf —— 快速上手

protobuf —— 快速上手 创建 .proto 文件添加注释指定proto3语法package 声明符定义消息&#xff08;message&#xff09; 定义消息字段字段定义基本格式字段名称命名规范字段类型字段唯一编号示例 转换关系示例&#xff1a;增加姓名和年龄字段 字段唯一编号字段编号范围编码效…

ADC模数转换器的简介及参数详解

ADC全称是Analog-to-Digital Converter模数转换器&#xff0c;一般我们把模拟信号(Analog signal) 用A来进行简写&#xff0c;数字信号(digital signal) 用D来表示。是用于将模拟形式的连续信号转换为数字形式的离散信号的一类设备。 今天我们主要说ADC的参数&#xff0c;我们把…

安全阀检测要求标准:如何提高检测效率与准确性?

安全阀&#xff0c;作为承压设备的重要保护元件&#xff0c;其性能的稳定性和可靠性直接关系到设备的运行安全。 因此&#xff0c;对安全阀进行定期、规范的检测显得尤为重要。接下来&#xff0c;佰德将围绕安全阀的检测要求标准&#xff0c;从检测前准备工作到检测报告与记录…

【网络服务】正向代理和反向代理到底是什么意思

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;程序猿、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 前言 在学习和…

kubeadm引导欧拉系统高可用的K8S1.28.X

文章目录 一. 核心组件架构二. 有状态与无状态应用三. 资源对象3.1 规约与状态3.2 资源的分类-元数据,集群,命名空间3.2.1 元数据3.2.2 集群资源 3.3 命名空间级3.3.1 pod3.3.2 pod-副本集3.3.3 pod-控制器 四. Kubeadm安装k8s集群4.1 初始操作4.2 ~~所有节点安装Docker&#x…