Ruby Ruby 物联网编程闲话

rocLv · 2021年08月06日 · 最后由 rocLv 回复于 2021年08月11日 · 752 次阅读

昨天在 Ruby 大会的微信群里聊起来物联网编程,就说今天写一篇。

首先从硬件本身聊起。

硬件领域基本都是 C 语言或者汇编语言的天下,其他语言基本上也就是划划水的存在。

大家都知道,ruby 除了我们常用的 CRuby 版本外,还有 JRuby 等等各种实现版本,估计比较少人知道的是mruby

直接使用mruby 还是有点难的,下面的下面会介绍一下。

Arduino

Arduino 系列是针对硬件爱好者的开发的,通过这个项目mruby-arduino 可以用 Ruby 开发。

曾经试了一下,因为 Arduino 本身提供的语法也很简单,所以基本上就相当于玩了一下而已。

ESP

ESP 系列本身在工业领域用的比较多,现在也有一些类似于玩具一样的产品,比如说 ESP-EYE 等,可玩性很强。可以运行 TensorFlow Lite,实现离线人脸识别,除了速度慢没别的缺点。

有个开源的项目,mrubyc-esp32-arduino 可以实现在 ESP 系列硬件上用 ruby 编程,不过环境安装在国内实在是费劲。

但是 ESP 系列的可玩性很强,有个 micro-python 的项目,和 Ruby 相比起来更有优势。

micro:bit

另外还有针对BBC micro:bit v2的项目。

再说说物联网的协议

现在比较流行的有 NB-IoT 和 Lora 两种,当然还有其他的,有兴趣的可以自行百度一下。

NB-IoT

NB-IoT,全称是 Narrow Band Internet of Things,窄带物联网,它的原型是 2014 年 5 月华为和沃达丰联合提出的 NB M2M 技术,随后在 2015 年 5 月与高通提出的 NB OFDMA 技术融合成为 NB-CIOT 技术,之后又于 2015 年 9 月与爱立信公司提出的 NB-LTE 技术融合,形成了我们现在认识的 NB-IoT,并在 3GPP 上正式立项。至 2016 年 6 月,NB-IoT 核心标准冻结,但相关特性仍在持续演进中。

NB-IoT 的优点是不需要自己组网,直接依托运营商网络就可以。缺点就很显然,数据都到了运营商手里了,当然也可以自行对数据进行加密。之前在一家公司负责和华强北对接,他们就对自己的数据进行了封装,运营商如果没有协议是很难自行破解的(其实也很简单,估计就是数据没什么价值,因为除了自定义协议外并没有进行数据加密)。

和华强北合作的项目用的是电信的 NB-IoT 平台,可以通过 REST 接口拉取,也可以用 Apache Pulsar Client SDK 自行拉取。

很多城市的电动车防盗就是通过 NB-IoT 实现的,利润你懂得~

国家准备在未来几年,健全 NB-IoT 网络,覆盖城乡农村,加强在农业等领域应用。

Lora 也是流行的一种物联网协议。

Lora 的好处是 Lora 所用的无线电频率连业余频率都不是,所以组建 Lora 网络不需要相关部门批准。缺点就是自己组网成本较高。

一台 Lora 发射器成本不到一千元,可以覆盖大约空旷地带 10 公里的范围,实际上受建筑物的影响,可能每层都需要一台中继,所以实现的成本还是很高的。

目前在燃气抄表等领域,都有应用 Lora 的案例,和 NB-IoT 相比应用比较小。

优点是完全自主可控。

4G/5G/Wifi 直连

这没啥好说的

Ruby 物联网实现

我们使用的是EventMachine,好像之前停了一段时间之后又活了。

核心的原理就是物联网硬件通过 TCP Socket 发送到指定服务器,服务器通过 Nginx 做了一个负载均衡

因为物联网硬件是通过 4G 网络连接的,所以物联网硬件的 IP 是动态的。为了保持服务器和硬件之间的连接,需要定时发送心跳。

然后通过EventMachine来保存连接,需要的时候发送指令等。

以下的代码好久没看了,仅供参考:)

require 'eventmachine'

class SaleServer < EM::Connection
  include Wanfeng
  extend Wanfeng

  @@connections = {}
  @@logger = Logger.new("#{Rails.root}/log/vending_machines.log")

  def initialize
    @@last_heart  = Time.zone.now
  end

  def self.connections
    @@connections
  end

  def self.deliver_goods_data(machine, row, column, count, password="111111")
    sale(machine, rand(100), row, column, count, password).pack("C*")
  end

  def post_init
    @@logger.info "connected  "
  end

  def receive_data data
    @received_data = data.unpack('H*').first
    if valid_imei?
      add_clients
      log_received_data
      show_connections
      reply_heart
    end
  end

  def unbind
    @@connections.delete(self)
    @@logger.info "#{imei} disconnected\n"
  end

 private

 def imei
   get_machine_code(@received_data)
 end

 def valid_imei?
   !!(imei =~ /\A\d+\z/) && (imei&.size == 15)
 end

 def add_clients
   $redis.hmset('vending_machines', imei, Time.zone.now )
   @@connections[imei] = self
 end

 def log_received_data
   last_time =  "#{Time.now - @@last_heart} seconds"
   @@last_heart = Time.now

   @@logger.info "Received:\t#{last_time}\t#{@received_data}"
 end

 def reply_heart
   unless imei.present?
     @@connections[imei].send_data(heart(imei, "111111").pack("C*"))
   end
 end

 def show_connections
   @@connections.each do |k, v|
     @@logger.info "Connection: #{k}: #{v} "
   end
 end

end

这个是一段控制售货机的代码,出货指令是通过ActiveJob 实现的:

class AutosaleJob < ApplicationJob
  queue_as :default

  def perform(machine, row, column, count)
    SaleServer.connections[machine].send_data(
      SaleServer.deliver_goods_data(machine, row, column, count)
    )
  end
end

如果用的是 Passenger 的话,需要在config/application.rb 中这样启动EventMachine,没有这个if defined?(PhusionPassenger)逻辑会报什么错,我已经忘了 😂 :

# config/application.rb
    ...
    config.after_initialize do
      if defined?(PhusionPassenger)
        PhusionPassenger.on_event(:starting_worker_process) do |forked|
          if forked && EM.reactor_running?
            EM.stop
          end
          Thread.new {
            EM.run {
              EM.start_server '0.0.0.0', 4000, SaleServer
            }
          }

          Signal.trap("INT")  { EM.stop }
          Signal.trap("TERM") { EM.stop }
        end
      end

    end

   ....

出货调用逻辑:

AutosaleJob.perform_now(params[:code], row, column, amount)

这样你就能在夏天喝上王老吉了 :)

总结

在开发单片机原型的时候,用 Ruby 快速验证是可以的,不过你必须要懂点 C 才行,不然寸步难行。

在服务器开发方面,用 Ruby 实现是没有问题,而且运行也非常稳定。

服务器性能优化,最重要的是找到影响性能的瓶颈。

曾经做过一个中国全海域风能评估的项目,绝大部分代码是用 Ruby 写的,只有统计的一小点用的是 C。

这样既满足了需求,也大大提高了开发的速度和效率。

感谢分享 👍

MQTT 呢,不知道楼主有没有实战的经验。

tim_lang 回复

简单用了一下,mqtt 的工具还比较多,也有挺多可视化工具。我感觉和消息队列的有点像。

无论是 http,rpc,mqtt 这些,其实编程的时候感觉不是特别的明显。

主要可以了解一下具体的特性和使用场景,以及相关生态

需要 登录 后方可回复, 如果你还没有账号请 注册新账号