NUCLEO-F767ZI以太网功能实现笔记本电脑不开盖开机

NUCLEO-F767ZI以太网功能实现笔记本电脑不开盖开机

不想打开笔记本盖子按开机按钮开机?可以使用Wake-on-LAN远程唤醒。这里展示怎么用NUCELO-F767ZI以太网功能发送MagicPacket唤醒笔记本电脑。

http://blog.csdn.net/zoomdy/article/details/54799462
mingdu.zheng at gmail dot com

缘起:不想开盖按电源按钮

因为有一台23吋显示器,笔记本电脑在家里基本上当台式机用。每次开机的时候都要打开翻盖按一下电源按钮然后又合上,嫌开盖开机麻烦,然后就在网上找不开盖就能开机的方法,唯一靠谱的方案就是Wake-on-LAN,即局域网唤醒,简称WOL。正好有一块NUCLEO-F767ZI开发板,这是一款带以太网的开发板,可以用来发送MagicPacket唤醒我的笔记本电脑。

这里写图片描述

设置BIOS

要使用Wake-on-LAN功能,首先要进入BIOS打开Wake-on-LAN功能,不同的机器其设置位置可能不同,进BIOS找一找。也有可能不被支持。

验证Wake-on-LAN可以工作

这里以Ubuntu 12.04为例,Windows环境可以问度娘。首先使用ethtool工具检查需要被唤醒的机器是否正确打开了Wake-on-LAN功能,请注意ethtool工具输出的两条信息 Supports Wake-on: pumbgWake-on: g,Supports Wake-on说明是否具备Wake-on-LAN功能,Wake-on为g说明已经打开了Wake-on-LAN,如果Wake-on为d说明Wake-on-LAN被关闭了,更具体的内容可以 man ethtool

sudo apt-get install ethtool
sudo ethtool eth0
Settings for eth0:
	......
	Supports Wake-on: pumbg
	Wake-on: g

关闭需要被唤醒的机器,然后在另外一台电脑使用wakeonlan工具唤醒。

sudo apt-get install wakeonlan
wakeonlan 28:D2:44:3E:07:56
Sending magic packet to 255.255.255.255:9 with 28:D2:44:3E:07:56

如果设置没有错的话,稍等几秒钟就可以看到机器被唤醒了。

唤醒的原理

wakeonlan命令会发送一个目标端口为9的UDP广播数据包:MagicPacket,待唤醒机器的网卡接收到MagicPacket后,就会唤醒计算机。MagicPacket的格式如下图所示,开头是6字节FF,后面复制16份待唤醒机器的MAC地址。

MagicPacket Format

关于Wake-on-LAN更详细的内容可以参考这里:https://wiki.wireshark.org/WakeOnLAN

用NUCLEO-F767ZI实现唤醒

如果要用另外一台电脑输入命令来唤醒我的笔记本电脑,那比开盖更麻烦啊!我的预期是:按下排插按钮就可以自动唤醒。正好我有一块NUCLEO-F767ZI开发板,这是一块带有以太网功能的开发板,可以用NUCLEO-F767ZI发送MagicPacket唤醒机器。

首先参考这篇http://blog.csdn.net/zoomdy/article/details/54784027将以太网功能跑通。

然后修改lwIP配置,使用静态地址方式,DHCP获取地址是需要时间的,静态地址可以快很多。

配置静态地址

编写代码,在NUCLEO-F767ZI上电后发送一个MagicPacket,这是一个UDP包,使用lwIP的udp_*系列API来实现。没有使用socket接口,也没有加入FreeRTOS,这是个简单的应用,简单一些就可以了。

完整的源代码请访问:https://git.oschina.net/zoomdy/Wake-on-LAN,这里给出关键代码。

/*
 * Src/wol.c
 * https://wiki.wireshark.org/WakeOnLAN
 *
 * Packet Format
 *  |Synchronization Stream   |Target MAC   |Password (optional)  |
 *  |6                        |96           |0, 4 or 6            |
 *
 * The Synchronization Stream is defined as 6 bytes of FFh.
 *
 * The Target MAC block contains 16 duplications of the IEEE address
 * of the target, with no breaks or interruptions.
 *
 * The Password field is optional, but if present, contains either 4
 * bytes or 6 bytes. The WakeOnLAN dissector was implemented to dissect
 * the password, if present, according to the command-line format that
 * ether-wake uses, therefore, if a 4-byte password is present, it will
 * be dissected as an IPv4 address and if a 6-byte password is present,
 * it will be dissected as an Ethernet address.
 */

#include <stdint.h>
#include <string.h>
#include "stm32f7xx_hal.h"
#include "lwip.h"
#include "lwip/udp.h"
#include "wol.h"

void Error_Handler(void);

// 被唤醒机器的MAC地址
static const uint8_t targetAddress[ETHARP_HWADDR_LEN] =
  { 0x28, 0xd2, 0x44, 0x3e, 0x07, 0x56 };

static void fillMagicPacket(uint8_t buf[])
{
  int i;

  memset(&buf[0], 0xff, ETHARP_HWADDR_LEN);

  for (i = 0; i < 16; i++)
  {
    memcpy(&buf[(1 + i) * ETHARP_HWADDR_LEN], &targetAddress[0],
        ETHARP_HWADDR_LEN);
  }
}

static void sendMagicPacket(void)
{
  static struct udp_pcb *pcb = NULL;
  struct pbuf *pbuf = NULL;
  err_t err;

  if (pcb == NULL)
  {
    pcb = udp_new();
    if (pcb == NULL)
    {
      Error_Handler();
    }

    err = udp_connect(pcb, IP_ADDR_BROADCAST, 9);
    if (err != ERR_OK)
    {
      Error_Handler();
    }
  }

  pbuf = pbuf_alloc(PBUF_TRANSPORT, (1 + 16) * ETHARP_HWADDR_LEN,
      PBUF_RAM);
  if (pbuf == NULL)
  {
    Error_Handler();
  }

  fillMagicPacket(pbuf->payload);

  err = udp_send(pcb, pbuf);
  if (err != ERR_OK)
  {
    Error_Handler();
  }

  pbuf_free(pbuf);
  pbuf = NULL;

#if 0 // 不要释放pcb,后面还要用
  udp_remove(pcb);
  pcb = NULL;
#endif

}

void WOL_Process(void)
{
  static int fired = 0;
  uint32_t tick;

  tick = HAL_GetTick();
  if(fired == 0 && tick >= 2000) // 上电2秒后发送Magic Packet
  {
    sendMagicPacket();
    HAL_GPIO_WritePin(GPIOB, LED_RED_Pin, GPIO_PIN_SET);
    fired = 1;
  }
}

void BTN_Process(void)
{
  static uint32_t tick_prev = 0;
  static uint32_t btn_state = 0;
  uint32_t tick;

  tick = HAL_GetTick();
  if(tick != tick_prev)
  {
    tick_prev = tick;
    btn_state <<= 1;
    if(HAL_GPIO_ReadPin(BTN_USER_GPIO_Port, BTN_USER_Pin))
    {
      btn_state |= 1;
      HAL_GPIO_WritePin(GPIOB, LED_BLUE_Pin, GPIO_PIN_SET);
      HAL_GPIO_WritePin(GPIOB, LED_RED_Pin, GPIO_PIN_RESET);
    }
    else
    {
      btn_state |= 0;
      HAL_GPIO_WritePin(GPIOB, LED_BLUE_Pin, GPIO_PIN_RESET);
    }

    if(btn_state == 0xffff0000) // 按钮释放立即发送Magic Packet
    {
      sendMagicPacket();
      HAL_GPIO_WritePin(GPIOB, LED_RED_Pin, GPIO_PIN_SET);
    }
  }
}

void LED_Process(void)
{
  if(HAL_GetTick() & 0x100)
  {
    HAL_GPIO_WritePin(GPIOB, LED_GREEN_Pin, GPIO_PIN_SET);
  }
  else
  {
    HAL_GPIO_WritePin(GPIOB, LED_GREEN_Pin, GPIO_PIN_RESET);
  }
}

/*
 * Src/main.c 仅给出main函数部分
 */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART3_UART_Init();
  MX_LWIP_Init();

  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
    MX_LWIP_Process();
    WOL_Process();
    BTN_Process();
    LED_Process();
    __WFE(); // Save 40mA
  }
  /* USER CODE END 3 */

}

最后

用NUCLEO-F767ZI做这么简单的工作是不是大材小用了?过了元宵节,买块NUCLEO-F207ZG,终于有理由买NUCLEO-F207ZG了。?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值