欢迎光临散文网 会员登陆 & 注册

基于ESP32开发智能物联网微型设备

2023-03-14 16:15 作者:jack-001  | 我要投稿

介绍

Picoclick 是一种微型设备,可用作物联网按钮。它是一种一键式设备,可让您控制智能家居或物联网中的不同内容。一个基本示例是通过按下按钮来切换灯泡。此外,Picoclick-C3 可以用作迷你开发板,因为它带有 FPC 扩展端口,可以连接其他硬件。


用例

该设备能够连接到 WiFi 网络并执行任务,例如发送消息或写入主题 (MQTT)。与接入点的连接设置需要一些时间(大约 2-3 秒),这对某些应用程序来说太长了——至少对我来说是这样。因此,我总是将我的 Picoclicks 与 ESPNOW 一起使用,ESPNOW 是 Espressif 自己开发的 WiFi 协议。它不需要连接到接入点,因此通信速度超快。然而,当使用 ESPNOW 协议时,需要有一个桥接器(基于 ESP 的设备)将这些消息转换为例如 MQTT 消息。

工作原理

Picoclick 是一款超低功耗设备 - 它可以通过软件关闭自己的电源,并且在不活动时仅消耗约 2µA(这仅归功于嵌入式电池保护)。所谓的电源锁存电路能够管理稳压器的启用。这可以通过两种方式完成:

  • 按钮。当按下按钮时,电压调节器将被启用,因此设备将被激活。

  • 闩锁。GPIO 或处理器的任何其他输出。

对于以前版本的 Picoclick,我选择了处理器的普通 GPIO。按下按钮时,处理器将启动,其首要任务是触发锁存器,该锁存器将保持设备的电源。如果设备应该关闭,锁存器可以接地并且电源将被禁用。这确保了处理器足够快来触发锁存器——否则设备将在没有任何操作的情况下关闭。

这是 Picoclick-C3 的黄金点。它不使用 GPIO 作为锁存器,而是使用嵌入式闪存的电源信号。关于 ESP32-C3 的数据表,在处理器深度睡眠期间电源将被停用以节省一些电量。此外,该电源将在处理器通电后立即出现。因此,要停用 Picoclick 设备,只需进入深度睡眠模式即可。(可以肯定的是:设备不会进入深度睡眠模式,因为它会预先断电)

除此之外,您不必为软件中的闩锁而烦恼,也不必将 ESP32-C3 配置得尽可能快(如 C3T)。因此不再需要使用 ESP-IDF 来刷写 Picoclick。Arduino 框架也将与该设备完美配合。

 

 

 

 

 

概述

顾名思义,Picoclick-C3 基于 ESP32-C3,这是一款运行频率高达 160MHz 的 32 位 RISC-V 处理器。

特征

·        Picoc尺寸:18x22mm

·        单片机:ESP32-C3FH4

·        单按钮界面

·        两个 APA102-2020 RGB LED

·        嵌入式电池保护

·        带状态 LED 的嵌入式电池充电

·        优化的电池监控

·        带有两个 GPIO 和电源引脚的扩展端口

·        超低功耗设备

·        包括一个板载芯片天线


正面

正面主要只有三样东西:12mm 按钮、两个 APA102-2020 LED 和 6p FPC 连接器。此外还有两个焊接跳线。




 

背面

背面是主要部件面。它拥有USB Type-C接口、ESP32-C3、电池充电以及电池保护电路、稳压器、天线及其匹配网络和电源锁存电路。

功能                            GPIO ESP32-C3                                   备注

APA102   SDI (LED)                       通用输入输出口7                                     输出

APA102 时钟 (LED)                        通用输入输出口6                                     输出

按钮状态                                         通用输入输出口5                                     输入

电池电压                                         通用输入输出口4                    输入,包括一个 1:1 分压器

电池电压触发                                 通用输入输出口3                                     输出

电池连接

电池连接


目前的功耗

下面所有的电流消耗都是测得的。

模式                                          平均电流                                               备注

WiFi已激活                                              74毫安                               峰值可达 200mA

WiFi 禁用且 LED 灯变暗                         27毫安

Picoclick 已停用                                       2微安

可以通过以下步骤降低活动模式下的电流消耗:

·        仅在真正需要时才打开 WiFi。

·        降低 CPU 频率。使用嵌入式时钟 (40MHz),您可以将频率设置为 10、20 和 40MHz。10MHz 仅适用于 LED 应用。

·        降低 APA102 LED 亮度。

跳线

有两个具有两种不同功能的焊接跳线。BOOT和USB跳线。


引导跳线

左侧跳线将 MCU 的引导引脚连接到 GND,从而强制进入 ESP32 的引导模式。

USB 跳线

右边的跳线将 USB 电压连接到电池电压,这样 Picoclick 就可以直接通过 USB 端口供电,而无需连接另一个电源。那时(当然)不使用电池充电器。

备注!:如果此跳线闭合,请勿将电池连接到 Picoclick,否则会毁坏 Picoclick、您的电池或两者。

 

 


 

软  件

重要的提示

Picoclick 的典型过程如下所示:

1.   1. 按下按钮将激活 Picoclick。

2.   2.将发送一条消息。

3.   3.一些 LED 的东西会发生。

4.   4.Picoclick 将自行停用。

根据使用情况和/或所使用的协议,可以或多或少地快速处理这四个步骤。例如,如果使用 ESP-NOW,可以在 500 毫秒内到达第 4 点。

如果您想将新代码上传到 Picoclick,那么激活设备很重要。这听起来很简单,但如果设备只激活了几百毫秒,那么您可能不会在正确的时间点开始上传过程。即使按住按钮的时间更长,一旦 MCU 进入深度睡眠模式,嵌入式闪存的电源也会被停用。

为了有足够的时间上传新代码,建议在进入深度睡眠模式之前使用循环。这个循环看起来像这样:

int counter = 0;

while(digitalRead(BUTTON_PIN) == 1){

    leds[0] = counter % 2 == 0 ? CRGB::Blue : CRGB::Black;

    leds[1] = (counter+1) % 2 == 0 ? CRGB::Blue : CRGB::Black;

    FastLED.show();

    delay(50);

    counter++;

}

 

// 添加一个循环,只要按下按钮就会等待进入深度睡眠。

// 一旦进入深度睡眠,USB 控制台就不再可用。

esp_deep_sleep_start();

}

 

因此只要按下按钮,设备就会交替闪烁两个 LED。此后将进入深度睡眠模式。

如果您没有遵循这些说明并且无法将代码闪存到您的 Picoclick,那么您必须按照下一章中的步骤进行操作。

引导跳线

Boot soldering jumper将ESP32的boot strapping pin连接到GND,再次上电即进入boot模式。

需要引导焊接跳线的原因:

·        您的代码进入深度睡眠模式的速度太快(如上所述)

·        您不小心上传了错误的代码(例如)切换 GPIO9

·        您的刷机过程在上传过程中崩溃

如果发生上述情况,那么您必须像这样进行:

1.   切断电源。如果您通过 USB 或扩展端口为 Picoclick 供电,则只需拔下它即可;如果你用电池供电,你必须拆焊它(至少一个连接)

2.   焊接或短接 PCB 按钮侧的引导跳线。

3.   连接电源(建议使用 USB 或扩展端口,否则必须再次拆焊电池)。

4.   如果在最后一步中尚未完成,请将 Picoclick 连接到您的计算机。

5.   按下 Picoclick 的按钮并点击 PlatformIO 中的上传按钮。

6.   如果完成,断开所有电源。

7.   拆焊引导跳线。

8.   连接你的最终电源(这里是可以再次连接电池的地方)。

 

 

获取电池电压

Picoclick-C3 具有优化的电池监控功能,在不使用时不会消耗任何电量。作为比较:即使 Picoclick 未处于活动状态,C3T 也需要大约 3µA 的电流。

要读取电池电压,您必须将状态ADC_ENABLE_PIN从高变为低,然后可以读取几毫秒的电压。读取电池电压后,返回该引脚的高电平状态以便之后再次读取电压是有用的。

下面的函数读取 ADC 连接到的模拟引脚,并返回过滤后的(100 除以 100 的总和)电池电压(以伏特为单位)。在最后一行代码中,原始模拟值将使用乘法器转换为电压值。如果需要,也可以添加恒定的线性偏移。

#define BAT_VOLT_MULTIPLIER   1.43

#define BAT_VOLT_OFFSET       0

 

float get_battery_voltage(){

  digitalWrite(ADC_ENABLE_PIN, LOW);

  delayMicroseconds(10);

  int sum = 0;

  for(int i=0; i<100; i++){

    sum = sum + analogRead(ADC_PIN);

  }

  float result = sum/100.0;

  digitalWrite(ADC_ENABLE_PIN, HIGH);

  return float(result) * BAT_VOLT_MULTIPLIER + BAT_VOLT_OFFSET;

}

串行输出

Picoclick 没有标准串行控制台,因为它使用 USB-CDC 接口与处理器通信。使用 写入标准控制台Serial.print("...")会将数据输出到未连接到 Picoclick 的 UART 接口。

尽管如此,仍然有一种方法可以使用串行控制台。这就像使用printf("...")instead of 一样简单Serial.print("...")。此外,串行控制台不需要使用Serial.begin(X).

将 Picoclick 的电池电压写入串行控制台的基本示例:

#include <Arduino.h>

#include <WiFi.h>

#include "config.h"

 

void setup(){

  pinMode(BUTTON_PIN, INPUT);

  pinMode(ADC_ENABLE_PIN, OUTPUT);

  pinMode(ADC_PIN, INPUT);

  analogReadResolution(12);

  digitalWrite(ADC_ENABLE_PIN, HIGH);

 

  btStop();

  WiFi.mode(WIFI_OFF);

 

  FastLED.addLeds<APA102, APA102_SDI_PIN, APA102_CLK_PIN, BGR>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  FastLED.setBrightness(160);

  delay(50);

 

  set_fastled(CRGB::Blue);

 

  printf("Setup done!\r\n");

}

 

void loop() {

  printf("Battery voltage: %i mV\r\n", int(get_battery_voltage()));

  delay(500);

}

请确保不要忘记将回车符 ( \r) 和换行符 ( \n) 添加到字符串的末尾。

要在 PlatformIO 中打开串行监视器,您必须按下左下角带有电源适配器的按钮。

 

ESP-NOW

ESP -NOW是低延迟应用程序的绝佳协议,因为无接入点通信可实现超快速消息传输。按下按钮和接收发送消息之间的延迟低至 200 毫秒。这非常适合智能家居解决方案,您可以立即看到结果(灯/插座亮起)。

将 ESP-NOW 与 Picoclicks 一起使用时,您必须使用基于 ESP 的桥将这些消息转换为另一种协议的消息。在大多数情况下,ESP-NOW 到 MQTT 桥是最好的选择。在讨论网桥之前,让我们先看看发送者代码。

ESP-NOW发送器

在这里,发送者是物联网按钮本身。一旦按下 Picoclick,一条消息将充满信息并发送到其目的地。之后 Picoclick 将再次停用。功能代码可能如下所示:

#include <Arduino.h>

#include <WiFi.h>

#include <esp_now.h>

#include <FastLED.h>

#include "config.h"

 

// ESPNOW packet structure.

// Can be modified but should be the same on the receivers side.

typedef struct struct_message {

  int id;

  int value;

  int battery_level;

  int single_tap_duration;

} struct_message;

 

typedef struct struct_message_recv {

    bool answer;

} struct_message_recv;

 

struct_message data;

struct_message_recv data_recv;

 

#define ESPNOW_ID 8888 // Random 4 digit number

uint8_t receiver_address[] = {0x10, 0x91, 0xA8, 0x32, 0x7B, 0x70}; // Mac address of the receiver.

 

bool espnow_answer_received = false;

 

void on_data_recv(const uint8_t * mac, const uint8_t *incomingData, int len) {

  memcpy(&data_recv, incomingData, sizeof(data_recv));

  espnow_answer_received = true;

}

 

void setup(){

  pinMode(BUTTON_PIN, INPUT);

  pinMode(ADC_ENABLE_PIN, OUTPUT);

  pinMode(ADC_PIN, INPUT);

  analogReadResolution(12);

  digitalWrite(ADC_ENABLE_PIN, HIGH);

 

  btStop();

  WiFi.mode(WIFI_STA);

 

  FastLED.addLeds<APA102, APA102_SDI_PIN, APA102_CLK_PIN, BGR>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  FastLED.setBrightness(160);

  delay(50);

 

  if(esp_now_init() != ESP_OK) {

    printf("Error initializing ESP-NOW\r\n");

    return;

  }

 

  set_fastled(CRGB::Blue);

 

  esp_now_peer_info_t peerInfo;

 

  memcpy(peerInfo.peer_addr, receiver_address, 6);

  peerInfo.channel = 0; 

  peerInfo.encrypt = false;

     

  if(esp_now_add_peer(&peerInfo) != ESP_OK){

    printf("Failed to add peer\r\n");

    return;

  }

 

  esp_now_register_recv_cb(on_data_recv);

 

  // Fill ESPNOW struct with values.

  data.id = ESPNOW_ID;

  data.value = 1;

  data.battery_level = int(get_battery_voltage());

  data.single_tap_duration = 1000;

 

  esp_now_send(receiver_address, (uint8_t *) &data, sizeof(data));

 

  // wait on espnow answer

  unsigned long t_wait_answer_start = millis();

  while(!espnow_answer_received && millis() <= t_wait_answer_start + 300){

    delayMicroseconds(1);

  }

 

  // This will reduce power consumption.

  WiFi.mode(WIFI_OFF);

  setCpuFrequencyMhz(10);

 

  CRGB col = espnow_answer_received ? CRGB::Green : CRGB::Red;

  set_fastled(col);

  delay(500);

 

  int counter = 0;

  while(digitalRead(BUTTON_PIN) == 1){

    set_fastled(counter % 2 == 0 ? CRGB::Blue : CRGB::Black, (counter+1) % 2 == 0 ? CRGB::Blue : CRGB::Black);

    delay(50);

    counter++;

  }

 

  set_fastled(CRGB::Blue);

  delay(500);

 

  // Add a loop which will wait as long as the button is pressed before entering deepsleep.

  // Once in deepsleep the USB console is not available anymore.

  esp_deep_sleep_start();

}

 

void loop() {

 

}

上面的代码还做了一件事,确保消息传递成功。在它发出包含所有信息的初始消息后,它等待接收设备的应答 300 毫秒:

while(!espnow_answer_received && millis() <= t_wait_answer_start + 300){

    delayMicroseconds(1);

}

如果执行接收回调,则该espnow_answer_received标志将设置为true,因此一旦发送方收到消息。如果不是这种情况,则 while 循环将在 300 毫秒后退出。为了向用户提供反馈,LED 将根据接收状态呈绿色或红色亮起。

CRGB col = espnow_answer_received ? CRGB::Green : CRGB::Red;

ESP-NOW 接收器

接收器可以是任何基于 ESP 的设备,但在这种情况下,我也使用 Picoclick。按下按钮后,接收器将开机并等待来自发送器的消息。在等待期间,LED 将循环显示所有颜色。

收到消息后,send_answer()将调用该函数。它使用接收到的发送方设备的 MAC 地址添加 ESP-NOW 对等点,发送应答消息,然后删除对等点。

当接收器设备像发送器一样将其 LED 变为绿色时,两个设备将几乎同时亮起绿色。

#include <Arduino.h>

#include <WiFi.h>

#include <esp_now.h>

#include <FastLED.h>

#include "config.h"

 

// ESPNOW packet structure.

// Can be modified but should be the same on the receivers side.

typedef struct struct_message {

  int id;

  int value;

  int battery_level;

  int single_tap_duration;

} struct_message;

 

typedef struct struct_message_recv {

    bool answer;

} struct_message_recv;

 

struct_message data;

struct_message_recv data_answer;

 

#define ESPNOW_ID 8888 // Random 4 digit number

uint8_t receiver_address[] = {0x10, 0x91, 0xA8, 0x32, 0x7B, 0x70}; // Mac address of the receiver. 10:91:A8:32:7B:70

 

uint8_t temp_address[6];

uint8_t last_recv_address[6];

 

String mac;

 

bool need_answer = false;

 

void on_data_recv(const uint8_t * mac, const uint8_t *incomingData, int len) {

  memcpy(&data, incomingData, sizeof(data));

  memcpy(temp_address, mac, 6);

  need_answer = true;

}

 

esp_now_peer_info_t peerInfo;

 

void send_answer(){

  memcpy(peerInfo.peer_addr, temp_address, 6);

  peerInfo.channel = 0; 

  peerInfo.encrypt = false;

     

  if(esp_now_add_peer(&peerInfo) != ESP_OK){

    printf("Failed to add peer\r\n");

    return;

  }

 

  data_answer.answer = true;

 

  esp_now_send(temp_address, (uint8_t *) &data_answer, sizeof(data_answer));

  if(esp_now_del_peer(temp_address) != ESP_OK){

    printf("Failed to delete peer\r\n");

    return;

  }

  memcpy(last_recv_address, temp_address, 6);

  memset(temp_address, 0, 6);

 

  set_fastled(CRGB::Green);

}

 

String mac_to_string(uint8_t *addr){

  String mac_str = String(addr[0], HEX) + ":" + String(addr[1], HEX) + ":" + String(addr[2], HEX) + ":"

    + String(addr[3], HEX) + ":" + String(addr[4], HEX) + ":" + String(addr[5], HEX);

  mac_str.toUpperCase();

  return mac_str;

}

 

void setup(){

  pinMode(BUTTON_PIN, INPUT);

  pinMode(ADC_ENABLE_PIN, OUTPUT);

  pinMode(ADC_PIN, INPUT);

  analogReadResolution(12);

  digitalWrite(ADC_ENABLE_PIN, HIGH);

 

  WiFi.mode(WIFI_STA);

 

  FastLED.addLeds<APA102, APA102_SDI_PIN, APA102_CLK_PIN, BGR>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  FastLED.setBrightness(160);

  delay(50);

 

  if(esp_now_init() != ESP_OK) {

    printf("Error initializing ESP-NOW\r\n");

    return;

  }

 

  set_fastled(CRGB::Blue);

 

  esp_now_register_recv_cb(on_data_recv);

 

  mac = WiFi.macAddress();

  // printf("MAC %s\r\n", mac.c_str());

 

  delay(500);

}

 

unsigned long led_timer = millis();

int hue1 = 0, hue2 = 0;

int brightness = 255;

 

void loop() {

  if(digitalRead(BUTTON_PIN) == 1){

    set_fastled(CRGB::Red);

    delay(1000);

    esp_deep_sleep_start();

  }

 

  if(need_answer){

    need_answer = false;

    send_answer();

    led_timer += 1000;

  }

 

  if(millis() >= led_timer + 15){

    led_timer = millis();

    set_fastled(CHSV(hue1, 255, brightness), CHSV(hue2, 255, brightness));

    hue1 = (hue1 + 1)%255;

    hue2 = (hue1 + 127)%255;

  }

}

ESP-NOW 到 MQTT 桥

以下代码是单个设备上 ESP-NOW 到 MQTT 的桥接。要使一切正常,您需要将路由器的频道更改为固定频道 1。有关该频道或 Home Assistant 集成的更多信息,您应该查看视频。

要运行此代码,您需要在顶部部分添加您的 WiFi 凭据,并在重新连接函数中添加您的 MQTT 凭据。

该代码将所有 ESP-NOW 消息转发为 MQTT 消息,以集成到 Home Assistant 中。主题由标识符“节点”和四位数字“ESPNOW_ID”生成。典型的主题可能如下所示:home/node1234/value

#include <Arduino.h>

#include <WiFi.h>

#include <esp_now.h>

#include <esp_wifi.h>

#include <PubSubClient.h>

#include <time.h>

 

const char* ssid = "YOUR_SSID";

const char* password = "YOUR_PWD";

const char* mqtt_server = "YOUR_MQTT_IP"; // Example: 192.168.1.4

 

WiFiClient espClient;

PubSubClient client(espClient);

 

String current_time_str = "";

 

const char* ntpServer = "pool.ntp.org";

const long  gmtOffset_sec = 3600;

const int   daylightOffset_sec = 3600;

struct tm timeinfo;

int current_seconds = 0;

 

int32_t channel;

 

#define LED_PIN     15 // Wemos S2: 15, ThingPlusS2: 13

 

typedef struct struct_message {

    int id;

    int value;

    int battery_level;

    int single_tap_duration;

} struct_message;

 

typedef struct struct_message_recv {

    bool answer;

} struct_message_recv;

 

uint8_t temp_address[6];

 

bool need_answer = false;

 

struct_message data;

struct_message_recv data_answer;

bool new_data_received = false;

bool new_data_to_mqtt = false;

unsigned long t_new_data_received = 0;

 

#define N_SINGLETAP_SLOTS   10

bool reset_after_single_tap = false;

unsigned long t_reset_single_tap[N_SINGLETAP_SLOTS];

int reset_after_single_tap_id[N_SINGLETAP_SLOTS];

int single_tap_duration[N_SINGLETAP_SLOTS];

 

 

int battery_percentage[] = {4200, 4150, 4110, 4080, 4020, 3980, 3950, 3910, 3870, 3850, 3840, 3820, 3800, 3790, 3770, 3750, 3730, 3710, 3690, 3610, 3270};

int get_battery_percentage(float mv){

  int battery_mv = int(mv);

  int perc = 0;

  for(int i=0; i<=20; i++) if(battery_mv > battery_percentage[20-i]) perc+=5;

  return constrain(perc, 0, 100);

}

 

String get_formatted_time(){

  String t = "";

  if(timeinfo.tm_hour < 10) t += "0";

  t += timeinfo.tm_hour;

  t += ":";

  if(timeinfo.tm_min < 10) t += "0";

  t += timeinfo.tm_min;

  t += ":";

  if(timeinfo.tm_sec < 10) t += "0";

  t += timeinfo.tm_sec;

  return t;

}

 

 

void on_data_recv(const uint8_t * mac, const uint8_t *incomingData, int len) {

  digitalWrite(LED_PIN, 1);

  memcpy(&data, incomingData, sizeof(data));

  memcpy(temp_address, mac, 6);

  need_answer = true;

 

  printf("\r\n");

  printf("Receivid packet from %X:%X:%X:%X:%X:%X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  printf("Data - ID: %i, Value: %i, Battery: %i, TapDuration: %i\r\n", data.id, data.value, data.battery_level, data.single_tap_duration);

  printf("\r\n");

 

  if(data.single_tap_duration > 0){

    reset_after_single_tap = true;

    bool slot_found = false;

    for(int i=0; i<N_SINGLETAP_SLOTS; i++){

      if(single_tap_duration[i] == 0 && !slot_found){

        single_tap_duration[i] = data.single_tap_duration;

        reset_after_single_tap_id[i] = data.id;

        t_reset_single_tap[i] = millis();

        slot_found = true;

      }

    }

  }

 

  new_data_received = true;

  new_data_to_mqtt = true;

  t_new_data_received = millis();

}

 

esp_now_peer_info_t peerInfo;

 

void send_answer(){

  memcpy(peerInfo.peer_addr, temp_address, 6);

  peerInfo.channel = 0; 

  peerInfo.encrypt = false;

     

  if(esp_now_add_peer(&peerInfo) != ESP_OK){

    printf("Failed to add peer\r\n");

    return;

  }

 

  data_answer.answer = true;

  esp_now_send(temp_address, (uint8_t *) &data_answer, sizeof(data_answer));

  delay(10);

  if(esp_now_del_peer(temp_address) != ESP_OK){

    printf("Failed to delete peer\r\n");

    return;

  }

  memset(temp_address, 0, 6);

}

 

 

void callback(char* topic, byte* message, unsigned int length) {

  // digitalWrite(LED_PIN, 1);

  String messageTemp;

 

  for (int i = 0; i < length; i++) {

    messageTemp += (char)message[i];

  }

  // digitalWrite(LED_PIN, 0);

}

 

void reconnect() {

  while (!client.connected()) {

    digitalWrite(LED_PIN, 1);

    if (client.connect("MQTTClient_new", "YOUR_MQTT_USER", "YOUR_MQTT_PWD")) {

      client.subscribe("home/#");

      digitalWrite(LED_PIN, 0);

    } else {

      printf("Cannot connect to MQTT Server - Restarting in 5s!\r\n");

      delay(5000);

    }

  }

}

 

int32_t getWiFiChannel(const char *ssid) {

  if (int32_t n = WiFi.scanNetworks()) {

      for (uint8_t i=0; i<n; i++) {

          if (!strcmp(ssid, WiFi.SSID(i).c_str())) {

              return WiFi.channel(i);

          }

      }

  }

  return 0;

}

 

void setup() {

  pinMode(LED_PIN, OUTPUT);

 

  WiFi.mode(WIFI_AP_STA);

 

  channel = getWiFiChannel(ssid);

  esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);

  printf("WiFi channel: %i\r\n", channel);

 

  printf("Connecting to: %s\r\n", ssid);

  WiFi.begin(ssid, password);

 

  while (WiFi.status() != WL_CONNECTED) {

    digitalWrite(LED_PIN, 1);

    delay(150);

    printf(".\r\n");

    digitalWrite(LED_PIN, 0);

    delay(150);

  }

 

  printf("WiFi connected with IP: %s\r\n", WiFi.localIP().toString().c_str());

 

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

 

  client.setServer(mqtt_server, 1883);

  // client.setCallback(callback);

 

  if (esp_now_init() != ESP_OK) {

    printf("Error initializing ESP-NOW\r\n");

    return;

  }

 

  esp_now_register_recv_cb(on_data_recv);

 

  reconnect();

 

  String payload_mac = String(WiFi.macAddress());

  client.publish("home/s2macaddr", payload_mac.c_str());

}

 

unsigned long t_send_time_running = 0;

 

void loop() {

  if(need_answer){

    need_answer = false;

    send_answer();

  }

 

  if(new_data_received){

    if(new_data_to_mqtt){

      new_data_to_mqtt = false;

 

      String topic_value = "home/node" + String(data.id) + "/value";

      String topic_battery = "home/node" + String(data.id) + "/battery";

      String topic_batterylevel = "home/node" + String(data.id) + "/batterylevel";

 

      client.publish(topic_value.c_str(), String(data.value).c_str());

      client.publish(topic_battery.c_str(), String(data.battery_level).c_str());

      client.publish(topic_batterylevel.c_str(), String(get_battery_percentage(data.battery_level)).c_str());

    }

 

    if(millis() >= t_new_data_received + 1000){

      digitalWrite(LED_PIN, 0);

      new_data_received = false;

    }

  }

 

  if(reset_after_single_tap){

    bool any_reset_open = false;

    for(int i=0; i<N_SINGLETAP_SLOTS; i++){

      if(single_tap_duration[i] > 0){

        any_reset_open = true;

        if(millis() >= t_reset_single_tap[i] + single_tap_duration[i]){

          printf("Send zero\r\n");

          String topic_value = "home/node" + String(reset_after_single_tap_id[i]) + "/value";

          client.publish(topic_value.c_str(), "0");

 

          single_tap_duration[i] = 0;

          reset_after_single_tap_id[i] = 0;

        }

      }

    }

    if(!any_reset_open){

      reset_after_single_tap = false;

    }

  }

 

  unsigned int start = millis();

  if(!getLocalTime(&timeinfo)){

    printf("Failed to obtain time\r\n");

  }

  unsigned int stop = millis();

 

  if(current_seconds != timeinfo.tm_sec){

    current_seconds = timeinfo.tm_sec;

    printf("Time: %s\r\n", get_formatted_time());

    if(timeinfo.tm_sec == 0){

      client.publish("home/current_time", get_formatted_time().c_str());

    }

  }

 

  if (!client.connected()) {

    reconnect();

  }

 

  client.loop();

}

运动传感器

运动传感器扩展板基于STMicroelectronics 的LIS3DHTR 。传感器使用 I2C 与 Picoclick 通信(SDA = GPIO2,SCL = GPIO8)。它有一个超低功率稳压器,可用于通过触发中断来激活 Picoclick。中断可以通过软件配置。

硬件

概述

技术制图

测量单位为毫米,网格为 0.5 毫米

·        印刷电路板:18 毫米 x 10 毫米

·        厚度:1mm

·        安装孔:3.2mm

·        圆角半径:2mm

原理图

软件

 

#include <Arduino.h>

#include <WiFi.h>

#include <FastLED.h>

#include <SparkFunLIS3DH.h>

#include <Wire.h>

#include "config.h"

 

LIS3DH lis(I2C_MODE, 0x19); //Default constructor is I2C, addr 0x19.

 

void configIntterupts();

 

void setup(){

  pinMode(BUTTON_PIN, INPUT);

  pinMode(ADC_ENABLE_PIN, OUTPUT);

  pinMode(ADC_PIN, INPUT);

  analogReadResolution(12);

  digitalWrite(ADC_ENABLE_PIN, HIGH);

 

  btStop();

  WiFi.mode(WIFI_OFF);

  setCpuFrequencyMhz(10);

 

  FastLED.addLeds<APA102, APA102_SDI_PIN, APA102_CLK_PIN, BGR>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  FastLED.setBrightness(160);

  delay(50);

 

  set_fastled(CRGB::Blue);

 

  Wire.begin(SDA_PIN, SCL_PIN);

  delay(100);

 

  lis.settings.accelSampleRate = 50;  //Hz.  Can be: 0,1,10,25,50,100,200,400,1600,5000 Hz

  lis.settings.accelRange = 2;      //Max G force readable.  Can be: 2, 4, 8, 16

 

  lis.settings.adcEnabled = 0;

  lis.settings.tempEnabled = 0;

  lis.settings.xAccelEnabled = 1;

  lis.settings.yAccelEnabled = 1;

  lis.settings.zAccelEnabled = 1;

 

  lis.begin();

 

  // int dataToWrite = B01001111;

  // lis.writeRegister(LIS3DH_CTRL_REG1, dataToWrite);

 

  // configIntterupts();

}

 

unsigned long t_sensor = millis();

 

void loop() {

  if(digitalRead(BUTTON_PIN) == 1){

    set_fastled(CRGB::Red);

    delay(500);

    esp_deep_sleep_start();

  }

 

  if(millis() >= t_sensor + 500){

    t_sensor = millis();

    float x = lis.readFloatAccelX();

    float y = lis.readFloatAccelY();

    float z = lis.readFloatAccelZ();

    printf("X: %f, Y: %f, Z: %f\r\n", x, y, z);

  }

}

 

void configIntterupts(){

  uint8_t dataToWrite = 0;

 

  // //LIS3DH_INT1_CFG  

  // //dataToWrite |= 0x80;//AOI, 0 = OR 1 = AND

  // //dataToWrite |= 0x40;//6D, 0 = interrupt source, 1 = 6 direction source

  // //Set these to enable individual axes of generation source (or direction)

  // // -- high and low are used generically

  // //dataToWrite |= 0x20;//Z high

  // //dataToWrite |= 0x10;//Z low

  // dataToWrite |= 0x08;//Y high

  // //dataToWrite |= 0x04;//Y low

  // //dataToWrite |= 0x02;//X high

  // //dataToWrite |= 0x01;//X low

  // lis.writeRegister(LIS3DH_INT1_CFG, dataToWrite);

 

  // //LIS3DH_INT1_THS  

  // dataToWrite = 0;

  // //Provide 7 bit value, 0x7F always equals max range by accelRange setting

  // dataToWrite |= 0x10; // 1/8 range

  // lis.writeRegister(LIS3DH_INT1_THS, dataToWrite);

 

  // //LIS3DH_INT1_DURATION 

  // dataToWrite = 0;

  // //minimum duration of the interrupt

  // //LSB equals 1/(sample rate)

  // dataToWrite |= 0x01; // 1 * 1/50 s = 20ms

  // lis.writeRegister(LIS3DH_INT1_DURATION, dataToWrite);

 

  //LIS3DH_CLICK_CFG  

  dataToWrite = 0;

  //Set these to enable individual axes of generation source (or direction)

  // -- set = 1 to enable

  //dataToWrite |= 0x20;//Z double-click

  dataToWrite |= 0x10;//Z click

  //dataToWrite |= 0x08;//Y double-click

  dataToWrite |= 0x04;//Y click

  //dataToWrite |= 0x02;//X double-click

  dataToWrite |= 0x01;//X click

  lis.writeRegister(LIS3DH_CLICK_CFG, dataToWrite);

 

  //LIS3DH_CLICK_SRC

  dataToWrite = 0;

  //Set these to enable click behaviors (also read to check status)

  // -- set = 1 to enable

  //dataToWrite |= 0x20;//Enable double clicks

  dataToWrite |= 0x04;//Enable single clicks

  //dataToWrite |= 0x08;//sine (0 is positive, 1 is negative)

  dataToWrite |= 0x04;//Z click detect enabled

  dataToWrite |= 0x02;//Y click detect enabled

  dataToWrite |= 0x01;//X click detect enabled

  lis.writeRegister(LIS3DH_CLICK_SRC, dataToWrite);

 

  //LIS3DH_CLICK_THS  

  dataToWrite = 0;

  //This sets the threshold where the click detection process is activated.

  //Provide 7 bit value, 0x7F always equals max range by accelRange setting

  dataToWrite |= 0x0A; // ~1/16 range

  lis.writeRegister(LIS3DH_CLICK_THS, dataToWrite);

 

  //LIS3DH_TIME_LIMIT 

  dataToWrite = 0;

  //Time acceleration has to fall below threshold for a valid click.

  //LSB equals 1/(sample rate)

  dataToWrite |= 0x08; // 0x08: 8 * 1/50 s = 160ms

  lis.writeRegister(LIS3DH_TIME_LIMIT, dataToWrite);

 

  //LIS3DH_TIME_LATENCY

  dataToWrite = 0;

  //hold-off time before allowing detection after click event

  //LSB equals 1/(sample rate)

  dataToWrite |= 0x0F; // 4 * 1/50 s = 160ms,

  lis.writeRegister(LIS3DH_TIME_LATENCY, dataToWrite);

 

  //LIS3DH_TIME_WINDOW

  dataToWrite = 0;

  //hold-off time before allowing detection after click event

  //LSB equals 1/(sample rate)

  dataToWrite |= 0x8F; // 16 * 1/50 s = 320ms

  lis.writeRegister(LIS3DH_TIME_WINDOW, dataToWrite);

 

  //LIS3DH_CTRL_REG5

  //Int1 latch interrupt and 4D on  int1 (preserve fifo en)

  lis.readRegister(&dataToWrite, LIS3DH_CTRL_REG5);

  dataToWrite &= 0xF3; //Clear bits of interest

  dataToWrite |= 0x08; //Latch interrupt (Cleared by reading int1_src)

  //dataToWrite |= 0x04; //Pipe 4D detection from 6D recognition to int1?

  lis.writeRegister(LIS3DH_CTRL_REG5, dataToWrite);

 

  //LIS3DH_CTRL_REG3

  //Choose source for pin 1

  dataToWrite = 0;

  dataToWrite |= 0x80; //Click detect on pin 1

  // dataToWrite |= 0x40; //AOI1 event (Generator 1 interrupt on pin 1)

  // dataToWrite |= 0x20; //AOI2 event ()

  //dataToWrite |= 0x10; //Data ready

  //dataToWrite |= 0x04; //FIFO watermark

  //dataToWrite |= 0x02; //FIFO overrun

  lis.writeRegister(LIS3DH_CTRL_REG3, dataToWrite);

 

  // //LIS3DH_CTRL_REG6

  // //Choose source for pin 2 and both pin output inversion state

  // dataToWrite = 0;

  // // dataToWrite |= 0x80; //Click int on pin 2

  // // dataToWrite |= 0x40; //Generator 1 interrupt on pin 2

  // //dataToWrite |= 0x10; //boot status on pin 2

  // //dataToWrite |= 0x02; //invert both outputs

  // lis.writeRegister(LIS3DH_CTRL_REG6, dataToWrite);

}


基于ESP32开发智能物联网微型设备的评论 (共 条)

分享到微博请遵守国家法律