首先从硬件本身聊起。
硬件领域基本都是 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。
这样既满足了需求,也大大提高了开发的速度和效率。