arduino iot_如何使用Arduino构建自定义IoT硬件

arduino iot

最近,我想为制造商创建基于Arduino的低功耗物联网(IoT)设备,其内置传感器可用于将传感器数据从任何位置传输到云,并可能控制恒温器等已连接的设备,灯,门锁和其他家庭自动化产品。 在整个过程中,我了解到创建一个新的IoT设备(从构思到原型再到最终产品)并不像我想象的那么简单,并且没有“准备就绪”的开发设备。 但是,通过弄清楚如何做到这一点,我创建了一种名为Siguino的新产品,这是一种开放源代码的 IoT电路板,我希望它将使其他人可以更轻松,更快地创建自己的IoT产品。

Siguino基于Arduino Pro Mini的低功耗版本,该版本具有板载传感器和天线,并用一块电池供电。 它还利用Sigfox (一种旨在将IoT设备连接到云的低功耗广域网)。

本文介绍了从非常杂乱的电路板(但可以正常工作)的原型到最终的,定制设计的印刷电路板(PCB)的过渡过程,其他人将有望使用该电路板。

arduino iot_如何使用Arduino构建自定义IoT硬件_第1张图片

原型1与最终原型相比

1.面包板

在所有优秀制造商项目开始之初 ,我首先准备了一个概念性电路。 这涉及确定您希望设备具有的功能以及将使用的组件。 我希望我的设备能够:

  • 低功耗并基于Arduino(例如,使用ATmega328P芯片的Arduino Pro Mini)
  • 包括一个Sigfox芯片,以便它可以通过Sigfox网络发送消息。 我选择WiSOL SFMR10芯片有两个原因:
    • 它是仅发送芯片而不是收发器,而且我没有双向通信的计划
    • 有一个可用的DevKit (对面包板和原型制作非常有用)
  • 有四个基本传感器:
    • 连接的恒温器的温度(SparkFun DS18B20 )
    • 所连接灯光的光强度(标准光电管)
    • 用于门打开/关闭的磁性检测“霍尔效应”,例如门是否已打开或打开(DigiKey AH9246-W-7 )
    • 运动检测可确保设备安全,跌倒检测,周边运动检测等等。   我对跳闸开关,水银开关等进行了试验,但决定使用加速度计(Adafruit LIS3DH )是制造商的最佳选择,因为它为开发板提供了原生的可能性。 (请注意,此组件的分线板不是低功耗的 ,尽管原始芯片是。)

结果是一组相当混乱(但实用!)的组件:

Prototype 1

一切正常后,我花了一些时间使用面包板跳线整理出更整齐的版本:

Prototype 2

2.编写Arduino代码

下一步是编写基本代码,使面包板设备执行我想要的操作。 其中一些是标准的,并包含在每个组件的现有示例代码中。 例如,使用DS18B20 测量温度的代码如下所示:



   
     
     
     
     
#include
#include

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature temp_sensor(&oneWire);

void setup(){
  Serial.begin(9600);
  temp_sensor.begin();

  Serial.println("DS18B20 Temperature Test\n\n");

  delay(300);//Let system settle

}//end "setup()"

void loop(){

  Serial.print("Requesting temperatures...");
  temp_sensor.requestTemperatures(); // Send the command to get temperatures
 
  Serial.print("Temperature is: ");
  float temp_reading = temp_sensor.getTempCByIndex(0);
  Serial.println(temp_reading);

  delay(1000);
}// end loop()

有许多第三方库提供了用于Arduino Pro Mini的低功耗使用的选项。 我选择了可​​在GitHub上找到的Rocket Scream库。 家庭自动化社区和Andreas Rohner提供了有关修改Arduino Pro Mini以降低功耗的良好信息。 该项目的样本用法为:



   
     
     
     
     
// **** INCLUDES *****

#include "LowPower.h"

void setup()

{

// No setup is required for this library

}

void loop()

{

// Enter power down state for 8 s with ADC and BOD module disabled

LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);

// Do something here

// Example: Read sensor, data logging, data transmission.

}

可以使用标准AT命令与WiSOL Sigfox芯片进行通信(产品数据表中包含基本示例)。 对于这个项目,我只需要两个功能:

  • 发送消息:我为低级AT命令编写了一个包装器,以使发送命令(例如测试设备)和消息变得更加容易:


   
     
     
     
     
String send_at_command(String command, int wait_time){
  altSerial.println(command);
  delay(wait_time);
  return recv_from_sigfox();
}

void test_sigfox_chip(){
  Serial.println("Sigfox Comms Test\n\n");
  altSerial.begin(9600);
  delay(300);//Let system settle
 
  Serial.println("Check awake with AT Command...");
  chip_response = send_at_command("AT", 50);  
  Serial.println("Got reponse from sigfox module: " + chip_response);
 
  Serial.println("Sending comms test...");
  chip_response = send_at_command("AT", 50);  
  Serial.println("Comms test reponse from sigfox module: " + chip_response);

  chip_response = send_at_command("AT$I=10", 50);  
  Serial.println("Dev ID reponse from sigfox module: " + chip_response);

  chip_response = send_at_command("AT$I=11", 50);  
  Serial.println("PAC Code reponse from sigfox module: " + chip_response);
}

//message send
chip_response = send_at_command("AT$SF=" + hex_bits, 10000);
Serial.println("Reponse from sigfox module: " + chip_response);
  • 进入低功耗(睡眠)模式:尽管该芯片还支持“深度睡眠”选项,但我选择了基本睡眠模式。 从〜1.5µA到<1µA似乎并不值得,因为1.5µA静态电流消耗对于我的目的来说是可以接受的。 睡眠/唤醒周期代码如下所示:


   
     
     
     
     
//Sigfox sleep mode enabled via AT$P=1 command
// to wake need to set UART port low (see AX-SIGFOX-MODS-D.PDF for further details)
void set_sigfox_sleep(bool go_sleep){
  String chip_response;
  if (go_sleep){
    //send go sleep AT command
    chip_response = send_at_command("AT$P=1", 100);  
    Serial.println("Set sleep response: " + chip_response);
  }else{
    //wake up sigfox chip
    altSerial.end();
    pinMode(TX_PIN, OUTPUT);
    digitalWrite(TX_PIN, LOW);
    delay(100);
    altSerial.begin(9600);    
  }
}

钻头包装:

我决定对Sigfox消息使用位打包 ; 由于Sigfox消息的最大长度为12个字节,因此最好将尽可能多的数据压缩到每个消息中。 例如,假设温度传感器返回的温度将在-40到+80摄氏度之间浮动。 C ++中的浮点数使用4个字节的内存,但是如果不需要,您不想用12个字节的消息中的4个字节来发送数字。 通常,您只需要知道温度值的一半精度,就可以将整个温度范围压缩为8位(1字节),因为将-40到+80的范围限制为一半度增量,只有240个可能的值,如下所示:



   
     
     
     
     
0b00000000 [0] = -40
0b00000001 [1] = -39.5
0b00000010 [2] = -39

0b11101111 [239] = 79.5
0b11110000 [240] = 80

为了节省更多空间,我将范围限制在-10至+50 C(半度精度),这需要7位的温度,加上5位的光水平(从0到1,000),1位用于打开/关闭或设备移动,以及4位消息序列号,这样我就可以发现任何丢失的消息。 因此,我的基本传感器只需要使用我的12个字节的可用消息空间中的18位,如下所示:

Bitpacked sensors schematic

我修改了一组位打包功能 ,该功能将获取所有传感器数据以及我想为每个传感器使用的位数,然后将它们打包为一个12字节的值:



   
     
     
     
     
#ifndef BITPACKER_H_INCLUDED
#define BITPACKER_H_INCLUDED

#include

#define BIT(n)                  ( 1UL<<(n) ) //UL = unsigned long, forces chip to use 32bit int not 16

#define BIT_SET(y, mask)        ( y |=  (mask) )
#define BIT_CLEAR(y, mask)      ( y &= ~(mask) )
#define BIT_FLIP(y, mask)       ( y ^=  (mask) )

/*
        Set bits        Clear bits      Flip bits
y        0x0011          0x0011          0x0011
mask     0x0101 |        0x0101 &~       0x0101 ^
        ---------       ----------      ---------
result   0x0111          0x0010          0x0110
*/

//! Create a bitmask of length \a len.
#define BIT_MASK(len)           ( BIT(len)-1 )

//! Create a bitfield mask of length \a starting at bit \a start.
#define BF_MASK(start, len)     ( BIT_MASK(len)<<(start) )

//! Prepare a bitmask for insertion or combining.
#define BF_PREP(x, start, len)  ( ((x)&BIT_MASK(len)) << (start) )

//! Extract a bitfield of length \a len starting at bit \a start from \a y.
#define BF_GET(y, start, len)   ( ((y)>>(start)) & BIT_MASK(len) )

//! Insert a new bitfield value \a x into \a y.
#define BF_SET(y, x, start, len)    \
    ( y= ((y) &~ BF_MASK(start, len)) | BF_PREP(x, start, len) )

namespace BitPacker {
    static uint32_t get_packed_message_32(unsigned int values[], unsigned int bits_used[], int num_vals){
        uint32_t retval = 0x0;
        int j = 0;
        for (int i=0;i             BF_SET(retval, values[i], j, j + bits_used[i]);
            j += bits_used[i];
        }
        return retval;
    }

    static uint64_t get_packed_message_64(unsigned int values[], unsigned int bits_used[], int num_vals){
        uint64_t retval = 0x0;
        int j = 0;
        for (int i=0;i             BF_SET(retval, values[i], j, j + bits_used[i]);
            j += bits_used[i];
        }
        return retval;
    }

}
#endif // BITPACKER_H_INCLUDED

3.原型电路

在为您的设备定制设计PCB电路之前,值得确定一个更小,更整洁的原型电路。 我选择了该电路的配电盘版本。 最终结果应该是电路更整洁,更紧凑,这对于帮助调整最终的PCB设计非常有用。 (这很重要,因为根据经验,PCB越大,其成本就越高。)它还可以很好地了解最终产品可能需要哪种外壳。

我还使用了Fritzing ,这是一款用于布置Stripboard或Veroboard电路的软件。 它允许您设计一个虚拟电路,您可以在剥离板上复制它。 我的原型电路在Fritzing中看起来像这样:

prototype circuit in Fritzing

导致此实际(工作)电路的原因:

arduino iot_如何使用Arduino构建自定义IoT硬件_第2张图片

4.设计和印刷PCB

为了设计PCB,我使用了Autodesk Eagle ,这是一款出色的软件,可以免费用于小板(<80cm),并且具有许多组件库(包括良好的第三方库,例如,所有SparkFun和AdaFruit组件)。

我从这些SparkFun教程中学到了有关Eagle的所有知识:

  • 安装和设置
  • 创建原理图
  • 电路板布局和布线

根据经验,我会建议一些技巧:

  • 经常保存!
  • 每次更改后,无论大小如何,都应始终进行设计规则检查 (并重新检查)。 倒入后重新检查,即使变化应该影响倒入。 (通过在电路板的外层通常用开放的未填充区域填充铜,然后将铜填充物和缝合过Kong连接到地面,形成铜地面浇筑物。对于没有实心参考平面的两层电路板,浇铸的地面很有用;由于电容性耦合,它可以减少串扰。)
  • 当使用非常小的组件(例如, FPGA表面安装组件)进行布线时,请尝试在组件下方没有任何Kong,以免在没有专业工具(例如回流焊)的情况下进行手工焊接或表面安装组件进行原型测试时出现问题烤箱,拾放机械等)。 确保手工涂敷的焊锡或焊锡膏不落在组件下方并流入下方的布线Kong(您看不到的地方)非常困难。 当路由这些组件中的一些组件时,也很容易忘记。

换句话说,不要这样做:

Don't do this

而是这样做:

arduino iot_如何使用Arduino构建自定义IoT硬件_第3张图片
  • 对于较大的组件,出于与上述相同的原因,请尽量不要在组件的支脚或焊盘附近设置布线Kong。

我最终的,完全布线的电路板布局如下所示:

arduino iot_如何使用Arduino构建自定义IoT硬件_第4张图片

5.焊接表面安装的组件

在项目开始之初,我的一个大未知数是如何构建包括表面安装组件(SMC)的原型。 使用电镀通Kong(PTH)组件进行原型制作(例如,面包板)要容易得多,但是由于SMC体积更小且更整洁,因此您不会为最终产品选择PTH组件。

当您使用理想的SMC组件设计PCB布局,进行印刷并想要将它们放在一起进行测试时会发生什么,但是您没有任何表面贴装机械(例如拾取和放置机器或回流焊炉)? 您可以构建自己的回流焊炉 ,但是如果您要构建自己的电路,我认为偏离重点的工作有点耗时。 而且,几乎没有必要,因为您可以通过足够的实践手工焊接几乎所有的SMC,并且可以使用价格相对便宜的焊锡气枪来简化工作。

我使用EEVBlog YouTube频道学习了SMC焊接的基础知识,最后我将所有部件手工焊接到0402组件(如果呼吸太小,您将失去它们!)。 有关上下文,请参见以下组件尺寸比较表:

component size comparison chart

Zerodamage ,通过Wikimedia Commons, CC BY 3.0

我不建议在电路中使用0402组件。 (我别无选择,因为它们是天线下面的射频网的一部分,并且较大的组件可能会影响天线的性能。)实际上,0602组件也非常小,很难焊接,但是有点练习这是非常可行的。 我建议第一批订购额外的PCB,纯粹是为了进行焊接,因为您很可能会把第一次尝试弄得一团糟。

所需的工具包括:

  • 烙铁:优质的烙铁绝对值得多花一点钱。 我从便宜的开始,几个星期后我放弃了,换了更好的一个 ,一切都变得容易得多。
  • 热风枪:我还买了热风枪 ; 尽管事实证明它的使用比我希望的要难(要使气压正确,以免您从电路板上吹掉小组件,这是一种艺术形式!),但它已经焊接了一些较小的(VFLGA)封装集成电路,就像LIS3DH一样,容易得多。 (尽管显然有可能,我什至不确定我将如何单独使用烙铁来做到这一点。)当您弄乱某些东西时,它还使拆卸部件变得容易。
  • 镊子:高质量,非常精细的镊子对拾取非常小的元件至关重要。
  • 放大镜/放大镜:对于放大焊接以检查不良的焊料,焊桥,斑点,漏掉的针脚等,我发现珠宝商的放大镜非常有用,最好使用内置灯。

6.测量功耗

功耗测量是过程中非常困难但非常重要的一部分。 我希望我的设备具有超低功耗,因此可以使用小电池(例如900mAh CR2)使用一年。 这意味着要确保静态电流(恒定电流消耗)尽可能小,降低到低µA范围,同时考虑到消息发送过程中偶尔会有较高的电流消耗。 尽管有许多方法可以评估电路的电流需求,但大多数方法对于极低端的分辨率都很差。 手动机制(例如跨电源线连接的电流表)使用起来很麻烦,而且只能提供在特定时间使用多少电流的快照(在某些情况下,React速度不足以进行任何可靠的测量)。

在我尝试过的各种选择中,最后唯一起作用的是Nordic Semiconductor的Power Profiler Kit (PPK)。 它不是太贵(它和底板的价格都在100美元左右),而且效果很好。 (我的一个抱怨是,即使它是一个Python程序,也无法使它在Linux上可靠地工作,因此我必须启动Windows才能使用它。)

PPK不仅可以产生低至极低分辨率(<1µA)的恒定功耗视图,而且还可以产生一个时间窗口的运行平均值(正是我计算电池寿命所需的时间):

Power usage

7.对ATmega引导加载程序进行编程

您可能已经焊接到PCB上的原始ATmega芯片可能没有使用正确的保险丝设置(请参见下文)或编程的引导程序进行硬编码,因此您可能需要对其进行配置以使电路板正确运行。 对于首次使用PCB的设计师/建造者来说,这令人惊讶地令人困惑!

设置从芯片供应商处收到的原始ATmega芯片时,要解决三个主要任务。 (注意:有关详细信息,请参阅ATmega328P,但其中许多内容也适用于ATmega系列的其他部件):

保险丝设置:

保险丝是非易失性位,定义了芯片行为方式的许多可编程方面。 一共有三个熔丝字节,每个熔丝字节有8位:低字节,高字节和扩展字节。 例如,它们控制什么类型的时钟驱动芯片或欠压检测器(BOD)触发的电压。 BOD会在设定电压下停止执行代码,以免功率太低时操作不可靠。

默认值是在工厂提供的芯片中设置的。 这些可能适合芯片的预期用途。 但是,如果没有,则需要进行更改。 这可以通过SPI总线使用合适的接口(例如Ardiuno Uno板)来完成。 这里和这里都有一些有关此过程的良好指南。

引导程序:

需要将运行项目应用程序所需的代码加载到芯片中。 通常,使用FTDI头设备通过USB将芯片连接到编程PC。 在这种情况下,芯片需要安装引导加载程序以促进此操作。 实际上,这是一个加载程序的程序,但是它是使用合适的接口通过SPI总线加载的。

对于此项目,我使用了一个单独的Arduino UNO来按如下方式引导ATmega芯片:

  1. 对于引导程序,请使用Nick Gammon的ATmega芯片编程器
  2. 下载ZIP文件
  3. 提取ATmega_Board_Programmer文件夹(例如,到Arduino IDE库目录)
  4. 打开ATmega_Board_Programmer草图
  5. 将标准Arduino Uno连接到PC
  6. 将板设置为“ Arduino / Genuino Uno”并设置正确的端口
  7. 上传ATmega_board_programmer草图
  8. 断开Uno与PC的连接,然后按以下步骤将其连接到目标芯片:
宇野 目标
D10 重启
D11 摩西
D12 味噌
D13 SCK
Gnd nd
+5V Vcc
  1. 将Uno重新连接到PC->设置端口->运行串行监视器115200波特
  2. 引导加载程序应立即运行并在串行监视器窗口中显示结果。 遵循串行窗口中的说明(例如,Load Bootloader的“ L”)
  3. 请注意,引导加载程序会将芯片设置为使用内部8MHz时钟。 如果您有外部晶体,则可以对其进行修改(请参见草图中的注释)

程序代码加载:

一旦芯片上安装了引导加载程序,就可以通过FTDI接口加载程序代码。 在开发人员PC上运行的Arduino IDE可以通过此接口将应用程序代码直接加载到芯片上。

8.印刷PCB,购买组件,制造和组装

要从面包板转移到批量生产,您将需要各种资源:

  • 硬件组件:要在电路板上做面包,您将需要诸如各种电阻器,电容器,传感器,集成电路等组件。您可以在诸如Amazon之类的主流站点上找到其中的一些组件,但我建议将某些特定于硬件的站点作为一个组件。更好的选择。 我主要用DigiKey ; Mouser和Farnell也不错。
  • PCB印刷:设计PCB并创建用于指定印刷方式的Gerber文件后 ,您将需要找到一家公司进行印刷。 SparkFun在“选择PCB制造商”下有一些建议可能值得一看。 我使用Multi-CB时发现它们非常好,及时且价格具有竞争力,尽管我不得不通过银行转帐付款,因为它们不提供在线支付选项。
  • PCB制造:对PCB进行完整的设计,购买和手工焊接组件以及测试最后一个原型之后,现在就可以批量生产了。 我收到了PCBCart的非常合理的报价,其中包括汇编和ATmega芯片编程。 由于我还没有生产电路板,因此我无法评论其质量或服务。

9.进行后端开发

因此,您已经构建了设备,并且该设备在Sigfox网络上(主要是向Sigfox服务器)发送了消息……现在!!! 您将如何处理这些消息以及如何处理它们?

Sigfox回调

要做的第一件事是让Sigfox服务器将设备收到的所有消息转发到您控制的Web服务器或服务。 Sigfox系统上有很多选择方法,但是我认为最简单的方法是构建自己的RESTful Web服务(如下所述),并让Sigfox服务器通过以下方式向您的新服务发出HTTP(S)请求:消息数据。 这可以在Sigfox后端中通过使用设备的回调机制来完成,在该机制中,您可以根据需要从包括原始消息数据在内的可用变量列表中指定发布的变量或URL参数:

Sigfox backend callback

RESTFul Web服务

RESTful Web服务是现代的API,在Web上无处不在。 创建它们的方法有很多,但是我决定使用Go编程语言,首先,因为它是我想学习的语言,其次,因为它很容易通过Docker进行部署。 Go中的Web服务(保存到MongoDB数据库)的基本结构如下所示:



   
     
     
     
     
// Handler for HTTP Post - "/sensordata"
// Register new sensor data
func NewSensorData(w http.ResponseWriter, r *http.Request) {
    var dataResource SensorDataResource
    // Decode the incoming Task json
    err := json.NewDecoder(r.Body).Decode(&dataResource)
    if err != nil {
        common.DisplayAppError(
            w,
            err,
            "Invalid Sensor Data format",
            500,
        )
        return
    }
    sensorData := &dataResource.Data
    context := NewContext()
    defer context.Close()
    c := context.DbCollection("SensorData")
    repo := &db.SensorDataRepository{c}
    // Insert a sensor data document
    repo.Create(sensorData)
    if j, err := json.Marshal(SensorDataResource{Data: *sensorData}); err != nil {
        common.DisplayAppError(
            w,
            err,
            "An unexpected error has occurred",
            500,
        )
        return
    } else {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusCreated)
        w.Write(j)
    }
}

您可能构建的用于对Sigfox服务器中的原始数据进行基本处理的大多数简单Web服务都具有类似的结构。

我发现对于Sigfox消息解析特别有用的东西是位解包(因为我之前在Arduino代码中使用位打包将尽可能多的数据压缩到Sigfox消息中)。 用于解压缩数据的相应Go代码如下所示:



   
     
     
     
     
func bit(n uint64) uint64 {
    return 1< }

func bit_set(y uint64, mask uint64) uint64 {
    return y | mask
}

func bit_clear(y uint64, mask uint64) uint64 {
    return y & ^mask
}

func bit_flip(y uint64, mask uint64) uint64 {
    return y ^ mask
}

func bit_mask(len uint64) uint64 {
    return bit(len) - 1
}

func Bf_mask(start uint64, len uint64) uint64 {
    return bit_mask(len) << start
}

func Bf_prep(x uint64, start uint64, len uint64) uint64 {
    return (x & bit_mask(len)) << start
}

func Bf_get(y uint64, start uint64, len uint64) uint64 {
    return (y>>start) & bit_mask(len)
}

func Bf_set(y uint64, x uint64, start uint64, len uint64) uint64 {
    return (y & ^Bf_mask(start, len)) | Bf_prep(x, start, len)
}

IFTTT整合

最后,就使您的设备完成除数据记录之外的其他工作而言,将其与其他设备或生态系统集成的最简单方法可能是通过If This Then That(IFTTT) ,这是许多不同的API和系统的结合。 将设备连接到IFTTT后 ,就可以访问现有的后续操作。 例如,“如果[您的设备发送X],然后[向Y发送电子邮件],或[使Alexa说Y],或[在Y房间打开Philips Hue灯],”或其他多种选择。

向前进

我在Siguino项目中的下一步工作是为其开发3D外壳,通过Sigfox设备认证计划,调整天线以充分利用它,以及资助和组织该设备的首次生产。

由于该项目的主要目的是学习技术,因此我已在GitHub上将所有软件代码和硬件开源。 如果您有任何疑问或发现此有价值的信息,请在评论中让我知道。

翻译自: https://opensource.com/article/17/12/how-build-custom-iot-hardware-arduino

arduino iot

你可能感兴趣的:(python,java,编程语言,人工智能,大数据)