×

连接100英尺以外的OLED屏幕和I2C传感器

消耗积分:0 | 格式:zip | 大小:0.50 MB | 2022-11-03

分享资料个

描述

需要在距离您的项目太远的传感器位置添加 OLED 屏幕以获取有意义的报告?

I2C 总线上连接的传感器太多?那么你需要IO 扩展器!使用1-Wire® 到 I2C适配器,可以根据需要在 1-Wire® 总线上添加任意数量的 I2C 总线。

需要多条 1-Wire® 总线?IO Expander最多可同时支持 16 个。

需要更多?然后将最多 255 个IO 扩展器连接在一起;IO 扩展器真正提供极致的连接性!

功能列表

  • 使用便宜的 128x64 OLED 屏幕。
  • 在单个 1-Wire® 总线上使用多个 OLED 屏幕。
  • 使用不同的 OLED 屏幕尺寸。
  • 根据需要添加尽可能多的 I2C 总线。
  • 永远不要用完 I2C 地址。只需添加另一个 I2C 总线。
  • 易于使用的图形命令。
  • 1-Wire® 标准和过载,可实现快速屏幕更新。
  • 无需 1-Wire® 驱动器。节省代码空间。
  • 无需 OLED 驱动程序。节省代码空间。
  • 不需要 OLED 字体。节省代码空间。
  • 无需 DS18B20 温度驱动器。节省代码空间。
  • 无需 I2C 传感器驱动程序。节省代码空间。
  • 不需要额外的电源。
  • 易于连接 RJ11 电话线。
  • 易于在 1-Wire® 总线上添加传感器。
  • 热插拔 1-Wire® 总线。

用于构建 OLED 演示的部件

接线图

pYYBAGNh7HCAN2tkAAOFZpehZrM013.jpg
 

俯视图

 
 

运行此演示的 Arduino 代码。

/* IO Expander OLED sketch
 *  
 *  Make sure you increase the SERIAL_RX_BUFFER_SIZE in Arduino/hardware/arduino/avr/cores/arduino/HardwareSerial.h
 *  from 64 to 256.
 */
 
#define SERIAL_RX_BUFFER_SIZE   256

#include 
#include 
#include "IOExpander.h"

#define FAHRENHEIT

#define OLED_SCREENS            5
#define TEMP_SENSORS            4
#define HUMIDITY_SENSORS        2

#define ONEWIRE_TO_I2C_ROM1     "i4s51"
#define ONEWIRE_TO_I2C_ROM2     "i4s2e"
#define ONEWIRE_TO_I2C_ROM3     "i4s24"
#define ONEWIRE_TO_I2C_ROM4     "i4sc0"
#define ONEWIRE_TO_I2C_ROM5     "i2see"

#define ONEWIRE_TEMP_CONVERSION "t4s0;tt"
#define TEMP_SENSOR1            "t4r4c"
#define TEMP_SENSOR2            "t4r57"
#define TEMP_SENSOR3            "t4r76"
#define TEMP_SENSOR4            "t4r0300"
#define HUMIDITY_SENSOR1        "st8"
#define HUMIDITY_SENSOR2        "s6t5"

#define INIT_OLED_SSD1306_32    "st10;si128,32"
#define INIT_OLED_SSD1306_64    "st10;si128,64"
#define INIT_OLED_SSD1106       "st13;si128,64"
#define INIT_OLED_SSD1309       "g1o0,10;s2t10;si128,64"

#define SERIAL_DEBUG

#ifdef SERIAL_DEBUG
SoftwareSerial swSerial(8,7);
#endif

struct TS {
  bool update;
  float temp;
  bool error;  
};

struct HS {
  bool update;
  float temp;
  float humidity;
  bool error;
};

int led = 13;

#ifdef FAHRENHEIT
#define C2F(temp)   CelsiusToFahrenheit(temp)
float CelsiusToFahrenheit(float celsius)
{
  return ((celsius*9)/5)32;
}
#else
#define C2F(temp)   (temp)
#endif

bool init_oled[OLED_SCREENS] = {true, true, true, true, true};
TS ts[TEMP_SENSORS];
HS hs[HUMIDITY_SENSORS];

void ReadTempSensor(TS* ts, char* ts_read)
{
  float temp = ts->temp;
 
  SerialCmd(ts_read);
  ts->error = !SerialReadFloat(&ts->temp);
  SerialReadUntilDone();
  ts->temp = roundf(ts->temp * 10) / 10;
  ts->update = (temp != ts->temp);
}

void ReadHumiditySensor(HS* hs)
{
  float temp = hs->temp;
  float humidity = hs->humidity;
 
  SerialCmd("sr");
  hs->error = !(SerialReadFloat(&hs->temp) && SerialReadFloat(&hs->humidity));
  SerialReadUntilDone();
  hs->temp = roundf(hs->temp * 10) / 10;
  hs->humidity = roundf(hs->humidity * 10) / 10;
  hs->update = (temp != hs->temp || humidity != hs->humidity);
}

void SerialPrintDecimal(const char* str, float decimal, char error)
{
  Serial.print(str);
  if (error) Serial.print(F("NA"));
  else Serial.print(decimal, 1);
  Serial.print("\"");
}

void SerialPrintUnits(void)
{
  Serial.print(",248,\""
  #ifdef FAHRENHEIT
  "F"
  #else
  "C"
  #endif
  "\"");  
}

void SerialDrawBorder(uint8_t height)
{
  if (height == 32) Serial.print(";sh0,0,128;sh0,31,128;sv0,1,30;sv127,1,30");
  else Serial.print(";sh0,0,128;sh0,63,128;sv0,1,62;sv127,1,62");
}

void setup() {
  Serial.begin(115200);
  Serial.setTimeout(5000);  // 5 sec delay between DHT22 reads
#ifdef SERIAL_DEBUG
  swSerial.begin(115200);
  swSerialEcho = &swSerial;
#endif  
  pinMode(led, OUTPUT);
  wdt_enable(WDTO_8S);
}

void loop() {
  uint8_t i;
  static uint32_t last_millis = 0;
  static TS ts[TEMP_SENSORS];
  static HS hs[HUMIDITY_SENSORS];
 
  while (Serial.available()) Serial.read(); // Flush RX buffer
  Serial.println();
  if (SerialReadUntilDone()) {
  // First do a 1-Wire temperature measurement on all DS18B20 sensors
    SerialCmdDone(ONEWIRE_TEMP_CONVERSION);

  // Read the Si7020 humidity sensor
  if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM4)) {
  if (SerialCmdDone(HUMIDITY_SENSOR1)) {
        ReadHumiditySensor(&hs[0]);
  }
  }
  else init_oled[3] = true;

  // Read DHT22 humidity sensor every 2 seconds
  if (millis() - last_millis >= 2000) {
      SerialCmdDone(HUMIDITY_SENSOR2);
      ReadHumiditySensor(&hs[1]);
      last_millis = millis();
  }

  // Read 1-Wire temperature sensors
    ReadTempSensor(&ts[0], TEMP_SENSOR1);
    ReadTempSensor(&ts[1], TEMP_SENSOR2);
    ReadTempSensor(&ts[2], TEMP_SENSOR3);
    ReadTempSensor(&ts[3], TEMP_SENSOR4);

  if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM1)) {
  if (init_oled[0]) {
  if (SerialCmdNoError(INIT_OLED_SSD1306_32)) {
          init_oled[0] = false;
          ts[0].update = true;
  }
  }
  if (!init_oled[0] && ts[0].update) {
        SerialPrintDecimal("st10;sc;sf1;sa2;sd63,8,\"", C2F(ts[0].temp), ts[0].error);
  if (!ts[0].error) SerialPrintUnits();
        SerialDrawBorder(32);
        SerialCmdDone(";sd");
        ts[0].update = false;
  }
  }
  else init_oled[0] = true;
 
  if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM2)) {
  if (init_oled[1]) {
  if (SerialCmdNoError(INIT_OLED_SSD1306_64)) {
          init_oled[1] = false;
          ts[1].update = true;
  }
  }
  if (!init_oled[1] && ts[1].update) {
        SerialPrintDecimal("st10;sc;sf2;sa2;sd63,19,\"", C2F(ts[1].temp), ts[1].error);
  if (!ts[1].error) SerialPrintUnits();
        SerialDrawBorder(64);
        SerialCmdDone(";sd");
        ts[1].update = false;
  }
  }
  else init_oled[1] = true;

  if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM3)) {
  if (init_oled[2]) {
  if (SerialCmdNoError(INIT_OLED_SSD1106)) {
          init_oled[2] = false;
          ts[2].update = true;
  }
  }
  if (!init_oled[2] && ts[2].update) {
        SerialPrintDecimal("st13;sc;sf2;sa2;sd63,19,\"", C2F(ts[2].temp), ts[2].error);
  if (!ts[2].error) SerialPrintUnits();
        SerialDrawBorder(64);
        SerialCmdDone(";sd");
        ts[2].update = false;
  }
  }
  else init_oled[2] = true;

  if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM4)) {
  if (init_oled[3]) {
  if (SerialCmdNoError(INIT_OLED_SSD1106)) {
          init_oled[3] = false;
          ts[3].update = true;
  }
  }
  if (!init_oled[3] && (ts[3].update || hs[0].update)) {
        SerialPrintDecimal("st13;sc;sf1;sa1;sd60,12,\"", C2F(hs[0].temp), hs[0].error);
        SerialPrintUnits();
        SerialPrintDecimal(";sd60,32,\"", hs[0].humidity, hs[0].error);
        Serial.print(",\"%\"");
        SerialPrintDecimal(";sd121,12,\"", C2F(ts[3].temp), ts[3].error);
        SerialPrintUnits();
        SerialDrawBorder(64);
        SerialCmdDone(";sd");
        ts[3].update = false;
        hs[0].update = false;
  }
  }
  else init_oled[3] = true;

  if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM5)) {
  if (init_oled[4]) {
  if (SerialCmdNoError(INIT_OLED_SSD1309)) {
          init_oled[4] = false;
          hs[1].update = true;
  }
  }
  if (!init_oled[4] && hs[1].update) {
        SerialPrintDecimal("st10;sc;sf1;sa2;sd63,12,\"", C2F(hs[1].temp), hs[1].error);
        SerialPrintUnits();
        SerialPrintDecimal(";sd63,32,\"", hs[1].humidity, hs[1].error);
        Serial.print(",\"%\"");
        SerialDrawBorder(64);
        SerialCmdDone(";sd");
        hs[1].update = false;
  }
  }
  else init_oled[4] = true;

#ifdef SERIAL_DEBUG
#endif  
  }
  else {
    digitalWrite(led, HIGH);  // blink Arduino led for IO Expander failure
    delay(500);
    digitalWrite(led, LOW);
    delay(500);

  for (i = 0; i < OLED_SCREENS; i++) init_oled[i] = true;
  }
  wdt_reset();
}

SSD1309 128x64 OLED 2.42"。

较大的 2.42" OLED 的文档不好,所以我添加了一些关于如何正确使用1-Wire® 到 I2C适配器的附加信息。

显示器默认为 SPI,因此您要做的第一件事就是将其转换为 I2C。这意味着您需要取出烙铁并移除并添加一些 SMD 0603 电阻器。

pYYBAGNh7J2AMDbTAACS0fYwHaM279.jpg
 
  • 取下 R4 并将其放在 R3 上。
  • 在 R5 和 R7 处添加 0 欧姆电阻。
  • 将跳线添加到 CS-DS。这将有效地为 0x3C 的 I2C 地址选择提供 GND DC。如果您想使用 0x3D 的备用 I2C 地址,请移除跳线并将 DC 连接到 VCC (3V3)。
  • 将一根电线从 RES 引脚连接到1-Wire® 到 I2C适配器上 PL1 (D2) 的未跳线引脚。这将用作 GPIO 线,以在 OLED 显示器上电后发送所需的复位脉冲。

此显示控制器需要在 VCC 稳定后发送 RES 引脚上的复位脉冲。根据SSD1309 显示控制器,您必须将 RES LOW 设置为至少 3us,然后再设置为 HIGH。在面板锁定的情况下访问此显示器上的重置线也很有用,您可以在其中重置和重新初始化它,而无需关闭整个系统的电源。

要使用 IO 扩展器发送复位脉冲,请在所选引脚上使用 gpio 命令并指定持续时间。这里我们使用引脚 [1] 上的 [g]pio 命令作为 [0] 的 [o] 输出或 [ 10]毫秒。持续时间之后,该引脚将保持高电平。

>g1o0,10
ok
>

较大的 2.42 OLED 显示器使用一个简单的内部开关电容电荷泵,可将输入的 3V3 转换为 OLED 的 15V 高电压驱动。显示器平均从 3.3V 电源汲取约 50mA 电流,但所需的浪涌电流,特别是在较长的电缆长度上,需要在输入 3V3 的 VCC 和 GND 之间添加一个额外的 470uF 电解电容器。确保将负极连接到 GND!如果您使用的是插头连接器,只需将电容器插入引脚即可。

对于较小的 SSD1106 1.3",它还使用内部电荷泵将输入的 3V3 转换为 7V 以驱动 OLED,因此对于更长的电缆长度,您还需要在输入的 3V3 处添加一个额外的 100uF 电解电容器。该演示使用分路器作为 1-Wire® 短截线,将1-Wire® 到 I2C适配器连接到 OLED 显示器和 I2C Si7021 传感器。

分路器用作 D1 和 D2 上的两个 1-Wire® 分接器,R1 和 R2 为 100Ω

pYYBAGNh7KOAQQ55AAAi1VJV2C0528.jpg
 

为了将尽可能多的 1-Wire® 传感器连接到一根导线上,OLED 屏幕和传感器作为短截线连接到 1-Wire® 总线,但在分支点存在阻抗不匹配。来自短截线末端的反射返回到主干线,仅延迟信号通过短截线长度所需的时间。这些反射可能会导致网络上的其他从站出现问题。与短截线串联的电阻器将降低失配的严重程度和反射能量的幅度。该电阻器减轻了主干上的短截线产生的反射的不利影响

可靠的长线 1-Wire® 网络指南

1-Wire协议

为了有效地运行 OLED 显示器,IO 扩展器支持在 100 英尺以上高达 75.47kHz 的主动驱动 1-Wire® Overdrive 速度!如果您需要走几百英尺,那么您仍然可以退回到较慢的标准速度 15.38kHz。像 DS18B20 这样的原生温度传感器不支持 Overdrive 速度,因此您可以混合具有不同速度的设备,IO 扩展器将自动改变速度以匹配同一 1-Wire 总线上的设备。Overdrive 速度低于连接到 OLED 屏幕的 I2C 总线上的 400kHz,但对于在传感器位置报告传感器状态来说已经足够快了。

为了使用 Overdrive 速度,您必须在尽可能靠近 CPU IO 引脚的地方使用 2.2k 的强上拉电阻。在这个演示中,我们使用了带有 2.2k 上拉电阻的引脚 4,但对于引脚 2,我们需要添加一个。最简单的方法是在 IO 扩展器 PCB 底部端口的 RJ11 插头上的引脚之间焊接一个 2.2k 0603 电阻。

pYYBAGNh7KmAQiPsAABJhwnjt0s259.jpg
 

IO 扩展器支持三种不同命令来控制 1-Wire® 设备。

o - [o] 线直接控制。
i - 1-Wire® [i]2c 控制。
t - 1-Wire® [t] 温度传感器。

在引脚 [4] 上的 [o]newire 总线上,[f]找到所有 ROM。

>o4f
28ffe835c3160376
28ffea20c316034c
28ff19f4c2160300
28fffd34c3160357
197a28030000002e
19ad200300000051
19ed200300000024
198f1103000000c0
>

[i] 2c 引脚 [4] 上的设备,[f] 查找所有 ROM。

>i4f
197a28030000002e
19ad200300000051
19ed200300000024
198f1103000000c0
>

[t] 引脚 [4] 上的温度设备,[f] 找到所有 ROM。

>t4f
28ffe835c3160376
28ffea20c316034c
28ff19f4c2160300
28fffd34c3160357
>

那么使用IO Expander到底有多难呢?以身作则,自行判断。

[t] 引脚 [4] 上的温度传感器 [s] 选择 ROM [76]。执行 [t] 温度转换 [t],最后执行 [t] 温度 [r] 读取。

>t4s76;tt;tr
28ffe835c3160376
ok
17.9375
>

就这么容易!

可热插拔!

IO 扩展器的独特功能之一是它旨在支持热插拔设备。这意味着,如果您的传感器或 OLED 显示屏出现故障,您无需关闭系统即可更换它。当它仍然通电时,移除 RJ11 插头并重新连接新的传感器或 OLED 屏幕。此外,为了实现这一点,您必须确保您的代码已被编写为检测和重新初始化。一些传感器没有必要的保护,所以这样做需要您自担风险。任何 1-Wire 连接都应该没问题,因为IO 扩展器上的 1-Wire 驱动程序是专门为支持这一独特功能而编写的。因此,立即获取您的 IO 扩展器并构建您的系统!


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.obk20.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'连接100英尺以外的OLED屏幕和I2C传感器',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.obk20.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);