怎么看网站是什么程序,一个购物网站多少钱,园林景观中企动力提供网站建设,网站建设费用写创意目录
1、软件架构
2、开发环境
3、软件功能
4、程序设计
4.1、初始化
4.2、主循环状态机
4.3、初始化模式
4.4、配置模式
4.5、运行模式
4.6、重启模式
4.7、升级模式
5、程序功能特点
5.1、日志管理
5.2、数据缓存队列 本篇博文开始讲解下位机插座节点的MCU软件…目录
1、软件架构
2、开发环境
3、软件功能
4、程序设计
4.1、初始化
4.2、主循环状态机
4.3、初始化模式
4.4、配置模式
4.5、运行模式
4.6、重启模式
4.7、升级模式
5、程序功能特点
5.1、日志管理
5.2、数据缓存队列 本篇博文开始讲解下位机插座节点的MCU软件程序是如何实现。
1、软件架构
下位机软件架构采用前后台控制系统使用状态机思维实现程序设计。 2、开发环境
开发环境使用Arduino IDEIDE安装过程可参见https://handsome-man.blog.csdn.net/article/details/121195905
智能插座的控制器是ESP8266需要在IDE中安装该开发包如下图所示 3、软件功能
下位机软件整功能如下图所示 4、程序设计
4.1、初始化
节点上电后会执行初始化初始化程序顺序执行代码如下所示 Init_Log();Log.verboseln(config start!);Log.verboseln(init IO);Init_IO();Log.verboseln(IO OK!);Log.verboseln(init EEPROM);Init_EEPROM();if(Device_VariableInitial(MODE1) STATUS_SUCCESS){Log.verboseln(EEPROM OK!);}else{Log.errorln(EEPROM ERROR!);}Log.verboseln(init data queue);Init_queue();Log.verboseln(data queue OK!);Log.verboseln(init WiFi and server);if(Init_WIFI() STATUS_SUCCESS){Log.verboseln(WiFi and server OK!);}else{Log.errorln(WiFi and server ERROR!);}Log.verboseln(init time);Init_Time();Log.verboseln(time OK!);Log.verboseln(init electrical parameter);// Init_BL0942(); // 串口初始化时已经初始化波特率Log.verboseln(electrical parameter OK!);Log.verboseln(config end!);program_state.run_state INIT_STATE;
初始化时候有两点需要注意
1、节点的日志打印和采集电参数据使用同一路UART在正式版本软件中为了避免出现数据错乱的问题需要将日志打印功能关闭使#define LOG_OFF 0。 /********************************************************************************** \brief 初始化log日志模块**** \param 无**** \retval 无********************************************************************************/
void Init_Log(void)
{Serial.begin(4800, SERIAL_8N1); // 4800bps 无校验Serial.println();Log.setPrefix(printPrefix); // set prefix similar to NLogLog.setSuffix(printSuffix); // set suffix Log.begin(LOG_LEVEL_VERBOSE, Serial);Log.setShowLevel(false); // Do not show loglevel, we will do this in the prefix#if LOG_OFFDeInit_Log();#endif
} 2、E2PROM使用ESP8266内置的Flash模拟。默认情况下每次线烧程序、OTA升级程序这部分存储的配置并不会覆盖或者更新只有上位机下发更新配置参数才会修改。如果想线烧程序更改配置需要先将标志位#define DEVICE_FLAG 0XAA55修改成非0XAA55的其他数值。
4.2、主循环状态机
在主循环中使用1ms周期调度维护软件状态机节点运行有5种状态模式初始化模式、配置模式、运行模式、重启模式和升级模式。节点默认处于运行模式代码如下所示 switch(program_state.run_state){// 初始化模式case INIT_STATE:Init_State();break;// 配置模式case CONFIG_STATE:Config_State();break;// 运行模式case RUN_STATE:Run_State();break;// 重启模式case RESET_STATE:Reset_State();break;// 升级模式case UPDATA_STATE:Updata_State();break;}
4.3、初始化模式
初始化模式中初始化一些变量数据。
初始化模式中有一个机制第一次连接立刻上传一次数据到服务器否则就按照默认的60秒周期上报数据第一次上报数据会很慢。代码如下所示 /********************************************************************************** \brief 初始化状态逻辑**** \param 无**** \retval 无********************************************************************************/
void Init_State(void)
{wifi_send_data.device_head DeviceParamSave.device_head FUNCTION_ID1;wifi_send_data.device_id DeviceParamSave.device_id;memcpy((wifi_send_data.software_version), (DeviceParamSave.software_version), 15);memcpy((wifi_send_data.hardware_version), (DeviceParamSave.hardware_version), 15);memcpy((wifi_send_data.release_time), (DeviceParamSave.release_time), 10);wifi_send_data.upload_cycle DeviceParamSave.upload_cycle;wifi_send_data.sample_cycle DeviceParamSave.sample_cycle;program_state.run_state_time (DeviceParamSave.upload_cycle * 1000); // 第一次连接立刻上传一次数据到服务器program_state.run_state RUN_STATE;
}
4.4、配置模式
配置模式可接收上位机下发的配置参数存储到节点E2PROM中。
配置模式有超时机制3分钟上位机未下发配置参数自动跳转到运行模式。
更新配置参数后由配置模式切换到重启模式节电重启。
代码如下所示 /********************************************************************************** \brief 配置状态逻辑**** \param 无**** \retval 无********************************************************************************/
void Config_State(void)
{program_state.config_state_time;if(program_state.config_state_time CYCLE_TIME_180SEC){LED_OFF;program_state.config_state_time 0;program_state.run_state RUN_STATE;Log.warningln(config timeout);Log.warningln(switch run state);}// 处理WiFi接收的数据if(wifi_receive_flag true){if(receive_data[0] DeviceParamSave.device_head FUNCTION_ID4){memcpy(wifi_receive_config, receive_data, sizeof(ReceiveConfig_t));if((wifi_receive_config.device_old_head (DeviceParamSave.device_head FUNCTION_ID4)) (wifi_receive_config.device_old_id (DeviceParamSave.device_id) || (wifi_receive_config.device_old_id 0XFFFF))){crc_temp check_crc16((uint8_t *)wifi_receive_config, wifi_receive_config.device_len - 2);if(wifi_receive_config.crc crc_temp){if(wifi_receive_config.device_config_type 0) // 默认配置{Log.verboseln(default setting...);DeviceParamSave.device_flag DEVICE_FLAG;if((wifi_receive_config.device_new_head ! 0) (wifi_receive_config.device_new_head ! DeviceParamSave.device_head)){DeviceParamSave.device_head wifi_receive_config.device_new_head;}else{Log.verboseln(DEVICE_HEAD 0 or invariant);}if((wifi_receive_config.device_new_id ! 0) (wifi_receive_config.device_new_id ! DeviceParamSave.device_id)){DeviceParamSave.device_id wifi_receive_config.device_new_id;}else{Log.verboseln(DEVICE_ID 0 or invariant);}if((strcmp(wifi_receive_config.software_version, ) ! 0) (strcmp(wifi_receive_config.software_version, DeviceParamSave.software_version) ! 0)){memcpy((DeviceParamSave.software_version), (wifi_receive_config.software_version), 15);}else{Log.verboseln(SW_VERSION null or invariant);}if((strcmp(wifi_receive_config.hardware_version, ) ! 0) (strcmp(wifi_receive_config.hardware_version, DeviceParamSave.hardware_version) ! 0)){memcpy((DeviceParamSave.hardware_version), (wifi_receive_config.hardware_version), 15);}else{Log.verboseln(HW_VERSION null or invariant);}if((strcmp(wifi_receive_config.release_time, ) ! 0) (strcmp(wifi_receive_config.release_time, DeviceParamSave.release_time) ! 0)){memcpy((DeviceParamSave.release_time), (wifi_receive_config.release_time), 10);}else{Log.verboseln(RELEASE_TIME null or invariant);}if((wifi_receive_config.upload_cycle ! 0) (wifi_receive_config.upload_cycle ! DeviceParamSave.upload_cycle)){DeviceParamSave.upload_cycle wifi_receive_config.upload_cycle;}else{Log.verboseln(UPLOAD_CYCLE 0 or invariant);}if((wifi_receive_config.sample_cycle ! 0) (wifi_receive_config.sample_cycle ! DeviceParamSave.sample_cycle)){DeviceParamSave.sample_cycle wifi_receive_config.sample_cycle;}else{Log.verboseln(SAMPLE_CYCLE 0 or invariant);}if((strcmp(wifi_receive_config.wifi_ssid, ) ! 0) (strcmp(wifi_receive_config.wifi_ssid, DeviceParamSave.wifi_ssid) ! 0)){memcpy((DeviceParamSave.wifi_ssid), (wifi_receive_config.wifi_ssid), 64);}else{Log.verboseln(WIFI_SSID null or invariant);}if((strcmp(wifi_receive_config.wifi_password, ) ! 0) (strcmp(wifi_receive_config.wifi_password, DeviceParamSave.wifi_password) ! 0)){memcpy((DeviceParamSave.wifi_password), (wifi_receive_config.wifi_password), 64);}else{Log.verboseln(WIFI_PASSWORD null or invariant);}if((strcmp(wifi_receive_config.server_ip, ) ! 0) (strcmp(wifi_receive_config.server_ip, DeviceParamSave.server_ip) ! 0)){memcpy((DeviceParamSave.server_ip), (wifi_receive_config.server_ip), 64);}else{Log.verboseln(SERVER_IP null or invariant);}if((wifi_receive_config.server_port ! 0) (wifi_receive_config.server_port ! DeviceParamSave.server_port)){DeviceParamSave.server_port wifi_receive_config.server_port;}else{Log.verboseln(SERVER_PORT 0 or invariant);}}else if(wifi_receive_config.device_config_type 1) // 恢复出厂设置{Log.verboseln(factory data reset...);DeviceParamSave.device_flag DEVICE_FLAG;DeviceParamSave.device_head DEVICE_HEAD;DeviceParamSave.device_id DEVICE_ID;memcpy((DeviceParamSave.software_version), SW_VERSION, strlen(SW_VERSION));memcpy((DeviceParamSave.hardware_version), HW_VERSION, strlen(HW_VERSION));memcpy((DeviceParamSave.release_time), RELEASE_TIME, strlen(RELEASE_TIME));DeviceParamSave.upload_cycle UPLOAD_CYCLE;DeviceParamSave.sample_cycle SAMPLE_CYCLE;memcpy((DeviceParamSave.wifi_ssid), WIFI_SSID, strlen(WIFI_SSID));memcpy((DeviceParamSave.wifi_password), WIFI_PASSWORD, strlen(WIFI_PASSWORD));memcpy((DeviceParamSave.server_ip), SERVER_IP, strlen(SERVER_IP));DeviceParamSave.server_port SERVER_PORT;}DeviceParamSave.crc check_crc16((uint8_t *)DeviceParamSave, sizeof(DeviceParamSave_t) - 2);Log.verboseln(DEVICE_HEAD:0X%X, DeviceParamSave.device_head);Log.verboseln(DEVICE_ID:0X%X, DeviceParamSave.device_id);Log.verboseln(SW_VERSION:%S, DeviceParamSave.software_version);Log.verboseln(HW_VERSION:%S, DeviceParamSave.hardware_version);Log.verboseln(RELEASE_TIME:%S, DeviceParamSave.release_time);Log.verboseln(UPLOAD_CYCLE:%d, DeviceParamSave.upload_cycle);Log.verboseln(SAMPLE_CYCLE:%d, DeviceParamSave.sample_cycle);Log.verboseln(WIFI_SSID:%S, DeviceParamSave.wifi_ssid);Log.verboseln(WIFI_PASSWORD:%S, DeviceParamSave.wifi_password);Log.verboseln(SERVER_IP:%S, DeviceParamSave.server_ip);Log.verboseln(SERVER_PORT:%d, DeviceParamSave.server_port);if(Device_SaveParam() STATUS_SUCCESS){// 成功响应wifi_send_state.state_id ((wifi_receive_config.device_old_head - DeviceParamSave.device_head) 4) STATUS_SUCCESS;program_state.config_state_time 0;program_state.run_state RESET_STATE; // 配置成功重启节点Log.verboseln(config successful);Log.verboseln(switch reset state);}else{// 失败响应wifi_send_state.state_id ((wifi_receive_config.device_old_head - DeviceParamSave.device_head) 4) STATUS_ERROR;Log.errorln(config fail);}}else{// 失败响应wifi_send_state.state_id ((wifi_receive_config.device_old_head - DeviceParamSave.device_head) 4) STATUS_ERROR;Log.errorln(verify error);}}else{// 失败响应wifi_send_state.state_id ((wifi_receive_config.device_old_head - DeviceParamSave.device_head) 4) STATUS_ERROR;Log.errorln(frame error);}// WiFi发送响应组包wifi_send_state.device_head DeviceParamSave.device_head FUNCTION_ID2;wifi_send_state.device_len sizeof(SendState_t);wifi_send_state.device_id DeviceParamSave.device_id;memcpy((wifi_send_state.software_version), (DeviceParamSave.software_version), 15);memcpy((wifi_send_state.hardware_version), (DeviceParamSave.hardware_version), 15);wifi_send_state.crc check_crc16((uint8_t *)wifi_send_state, wifi_send_state.device_len - 2);WIFI_send_data((char *)wifi_send_state, wifi_send_state.device_len);}// 清除数据缓存memset(receive_data, 0, wifi_receive_config.device_len); memset((char *)wifi_send_state, 0, wifi_send_state.device_len);memset((char *)wifi_receive_config, 0, wifi_receive_config.device_len);wifi_receive_flag false; // 处理完成后方可接收WiFi新数据}
}
4.5、运行模式
只有在运行模式下上位机才可以切换到配置模式、重启模式和升级模式其他模式暂不支持远程控制模式切换。
运行模式下可周期上报节点数据以及支持上位机控制继电器开关。
代码如下所示 /********************************************************************************** \brief 运行状态逻辑**** \param 无**** \retval 无********************************************************************************/
void Run_State(void)
{program_state.run_state_time;// 处理WiFi接收的数据if(wifi_receive_flag true){if(receive_data[0] DeviceParamSave.device_head FUNCTION_ID3){memcpy(wifi_receive_mode_data, receive_data, sizeof(ReceiveData_Mode_t));if((wifi_receive_mode_data.device_head (DeviceParamSave.device_head FUNCTION_ID3)) (wifi_receive_mode_data.device_id (DeviceParamSave.device_id) || (wifi_receive_mode_data.device_id 0XFFFF))){crc_temp check_crc16((uint8_t *)wifi_receive_mode_data, wifi_receive_mode_data.device_len - 2);if(wifi_receive_mode_data.crc crc_temp){if(wifi_receive_mode_data.switch_mode 0){program_state.run_state RUN_STATE;Log.verboseln(keep run state);}else if(wifi_receive_mode_data.switch_mode 1){RELAY_OFF; // 进入配置模式要断开继电器program_state.run_state CONFIG_STATE;Log.verboseln(switch config state);}else if(wifi_receive_mode_data.switch_mode 2){RELAY_OFF; // 进入升级模式要断开继电器program_state.run_state UPDATA_STATE;Log.verboseln(switch updata state);}else if(wifi_receive_mode_data.switch_mode 3){RELAY_OFF; // 进入重启模式要断开继电器program_state.run_state RESET_STATE;Log.verboseln(switch reset state);}// 成功响应wifi_send_state.state_id ((wifi_receive_mode_data.device_head - DeviceParamSave.device_head) 4) STATUS_SUCCESS;}else{// 失败响应wifi_send_state.state_id ((wifi_receive_mode_data.device_head - DeviceParamSave.device_head) 4) STATUS_ERROR;Log.errorln(verify error);}}else{// 失败响应wifi_send_state.state_id ((wifi_receive_mode_data.device_head - DeviceParamSave.device_head) 4) STATUS_ERROR;Log.errorln(frame error);}// WiFi发送响应组包wifi_send_state.device_head DeviceParamSave.device_head FUNCTION_ID2;wifi_send_state.device_len sizeof(SendState_t);wifi_send_state.device_id DeviceParamSave.device_id;memcpy((wifi_send_state.software_version), (DeviceParamSave.software_version), 15);memcpy((wifi_send_state.hardware_version), (DeviceParamSave.hardware_version), 15);wifi_send_state.crc check_crc16((uint8_t *)wifi_send_state, wifi_send_state.device_len - 2);WIFI_send_data((char *)wifi_send_state, wifi_send_state.device_len);}if(receive_data[0] DeviceParamSave.device_head FUNCTION_ID5){memcpy(wifi_receive_control_data, receive_data, sizeof(ReceiveData_Control_t));if((wifi_receive_control_data.device_head (DeviceParamSave.device_head FUNCTION_ID5)) (wifi_receive_control_data.device_id (DeviceParamSave.device_id) || (wifi_receive_control_data.device_id 0XFFFF))){crc_temp check_crc16((uint8_t *)wifi_receive_control_data, wifi_receive_control_data.device_len - 2);if(wifi_receive_control_data.crc crc_temp){if(wifi_receive_control_data.relay_state 0){RELAY_OFF;}else{RELAY_ON;}// 成功响应wifi_send_state.state_id ((wifi_receive_control_data.device_head - DeviceParamSave.device_head) 4) STATUS_SUCCESS;}else{// 失败响应wifi_send_state.state_id ((wifi_receive_control_data.device_head - DeviceParamSave.device_head) 4) STATUS_ERROR;Log.errorln(verify error);}}else{// 失败响应wifi_send_state.state_id ((wifi_receive_control_data.device_head - DeviceParamSave.device_head) 4) STATUS_ERROR;Log.errorln(frame error);}// WiFi发送响应组包wifi_send_state.device_head DeviceParamSave.device_head FUNCTION_ID2;wifi_send_state.device_len sizeof(SendState_t);wifi_send_state.device_id DeviceParamSave.device_id;memcpy((wifi_send_state.software_version), (DeviceParamSave.software_version), 15);memcpy((wifi_send_state.hardware_version), (DeviceParamSave.hardware_version), 15);wifi_send_state.crc check_crc16((uint8_t *)wifi_send_state, wifi_send_state.device_len - 2);WIFI_send_data((char *)wifi_send_state, wifi_send_state.device_len);}// 清除数据缓存memset(receive_data, 0, wifi_receive_control_data.device_len); memset((char *)wifi_send_state, 0, wifi_send_state.device_len);memset((char *)wifi_receive_mode_data, 0, wifi_receive_mode_data.device_len);memset((char *)wifi_receive_control_data, 0, wifi_receive_control_data.device_len);wifi_receive_flag false; // 处理完成后方可接收WiFi新数据}// 采集电压、电流和电耗统计设备有效运行时间// 在逻辑上设定采样时间要小于等于上传云端时间// 此项目中采样周期必须设定为1秒if((program_state.run_state_time % DeviceParamSave.sample_cycle) 0){Updata_BL0942();wifi_send_data.voltage getVoltage(); // 电压wifi_send_data.current getCurrent(); // 电流wifi_send_data.power getActivePower(); // 功率wifi_send_data.electricity getEnergy(); // 电量if(wifi_send_data.power 0.5) // 功率大于0.5W认为有负载{run_start_flag true; }else{run_start_flag false; }}// 上传数据到服务器if(program_state.run_state_time (DeviceParamSave.upload_cycle * 1000)){program_state.run_state_time 0; // 上传周期时间要大于采样周期时间hours run_time_ms / 3600000;minutes (run_time_ms % 3600000) / 60000;seconds (run_time_ms % 60000) / 1000;time_data String(hours) - String(minutes) - String(seconds);memcpy((wifi_send_data.run_time), time_data.c_str(), time_data.length());for(uint8_t i time_data.length(); i 12; i){wifi_send_data.run_time[i] 0x00;}wifi_send_data.device_len sizeof(SendData_t);wifi_send_data.crc check_crc16((uint8_t *)wifi_send_data, wifi_send_data.device_len - 2);WIFI_send_data((char *)wifi_send_data, wifi_send_data.device_len);}
}
4.6、重启模式
确保缓存区数据都发送出去并且断开WiFi和服务器连接后节点重启。代码如下所示 /********************************************************************************** \brief 重启状态逻辑**** \param 无**** \retval 无********************************************************************************/
void Reset_State(void)
{if(tk_queue_empty(send_dataqueue) true) // 确保发送缓存区的数据都发送后才可以重启{delay(3000); // 重启节点的ACK可能还未发送出去需要有延时DeInit_WIFI();ESP.restart();}
}
4.7、升级模式
当所有发送缓存区的数据都发送完成后才可以执行升级功能。
目前升级仅支持局域网升级升级前节点会发送升级的IP和端口给上位机。
升级超时时间默认设置为180秒超时后节点切换到重启模式。
代码如下所示 /********************************************************************************** \brief 升级状态逻辑**** \param 无**** \retval 无********************************************************************************/
void Updata_State(void)
{static bool state_flag false;if(Init_OTA() STATUS_SUCCESS){if(state_flag false){state_flag true;// WiFi发送升级IP和端口wifi_send_updata.device_head DeviceParamSave.device_head FUNCTION_ID6;wifi_send_updata.device_len sizeof(SendUpdata_t);wifi_send_updata.device_id DeviceParamSave.device_id;memcpy((wifi_send_updata.software_version), (DeviceParamSave.software_version), 15);memcpy((wifi_send_updata.hardware_version), (DeviceParamSave.hardware_version), 15);memcpy((wifi_send_updata.updata_ip), ota_ip, strlen(ota_ip));wifi_send_updata.updata_port OTA_PORT;wifi_send_updata.crc check_crc16((uint8_t *)wifi_send_updata, wifi_send_updata.device_len - 2);WIFI_send_data((char *)wifi_send_updata, wifi_send_updata.device_len);memset((char *)wifi_send_updata, 0, wifi_send_updata.device_len);}if(tk_queue_empty(send_dataqueue) true) // 确保发送缓存区的数据都发送后才可以升级{OTA_updata();}}program_state.updata_state_time;if(program_state.updata_state_time CYCLE_TIME_180SEC) {LED_OFF;program_state.updata_state_time 0;program_state.run_state RESET_STATE;Log.warningln(updata timeout);Log.warningln(switch reset state);}
}
5、程序功能特点
5.1、日志管理
下位机支持日志管理可自定义串口打印不同等级的日志。
不过打印日志的串口和驱动BL0942的串口共用一路所以在发布正式程序时需要屏蔽日志打印功能。
日志管理部分代码如下所示 /********************************************************************************** \brief 初始化log日志模块**** \param 无**** \retval 无********************************************************************************/
void Init_Log(void)
{Serial.begin(4800, SERIAL_8N1); // 4800bps 无校验Serial.println();Log.setPrefix(printPrefix); // set prefix similar to NLogLog.setSuffix(printSuffix); // set suffix Log.begin(LOG_LEVEL_VERBOSE, Serial);Log.setShowLevel(false); // Do not show loglevel, we will do this in the prefix#if LOG_OFFDeInit_Log();#endif
}
5.2、数据缓存队列
发送和接收数据支持FIFO缓存方式写入和读取数据可自定义缓存区大小。
本项目中程序基本是顺序结构运行不存在外部中断和定时任务对数据的干扰并且发送和接收数据的数据量也不是很大即使暂不使用FIFO缓存也可以满足使用要求。
数据缓存部分代码如下所示 /********************************************************************************** \brief 初始化数据缓存**** \param 无**** \retval 无********************************************************************************/
void Init_queue(void)
{// 清空缓冲区memset(send_dataqueue_pool, 0, SEND_DATAQUEUE_POOL_SIZE);memset(receive_dataqueue_pool, 0, RECEIVE_DATAQUEUE_POOL_SIZE);memset(serial_receive_dataqueue_pool, 0, SERIAL_RECEIVE_DATAQUEUE_POOL_SIZE);// 静态方式创建一个循环队列,存满不能再存tk_queue_init(send_dataqueue, send_dataqueue_pool, sizeof(send_dataqueue_pool), sizeof(send_dataqueue_pool[0]), false);tk_queue_init(receive_dataqueue, receive_dataqueue_pool, sizeof(receive_dataqueue_pool), sizeof(receive_dataqueue_pool[0]), false);tk_queue_init(serial_receive_dataqueue, serial_receive_dataqueue_pool, sizeof(serial_receive_dataqueue_pool), sizeof(serial_receive_dataqueue_pool[0]), false);
}