鳕鱼天空

This is Mr Wang's Tech Blog.

转:认识与实验Arduino的睡眠模式

原文引自:http://swf.com.tw/?p=525

根据Nick Gammon这位澳洲老兄,在Power saving techniques for microprocessors(微处理器省电技术)文章,於Arduino UNO Rev 3控制板执行底下的程式码:
 

void setup () {}

void loop () {}



所测量到的消耗电流量:
 

  • 採用9V电池,接电源插孔供电,约消耗55 mA
  • 用5V电源供电,约消耗46.6 mA



若用最精简的準系统(barebone)形式,例如,在麵包板上直接用ATmega828处理器和石英震盪器等少数零件组装的Arduino,仅消耗15.15 mA电流。

因為Arduino控制板上的USB序列埠转换晶片以及电压调节元件,都会消耗电力。

毕竟Arduino控制板是「原型开发板」,其用意是提供一个方便、好用的微电脑控制实验工具。实验成功之后,如果要长久保留作品或者需要节省电力,最好自製一个精简的Arduino板,或购买类似Arduino Pro Mini这种没有其他周边零件的板子。

Arduino的睡眠模式

Arduino像电脑和手机一样,也具备睡眠∕休眠∕待机功能。在睡眠状态下,系统几乎完全停止运作,只保留基本的侦测功能,因此只消耗少许电力。以电脑為例,在睡眠状态下,可被键盘按键或者网路讯息唤醒。

底下的程式一开始就让微控器进入睡眠状态,下文将採用名叫”Enerlib”的程式库,简化睡眠设定程式:
 

void setup () 
{
  
// 設定採用“Power-down”睡眠模式
  
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  
// 啟用睡眠模式
  
sleep_enable();
  
// 進入睡眠模式
  
sleep_cpu ();  

}

 
void loop () { }



这段程式在UNO R3控制板上,约消耗32.9 mA电流;但是在精简的「準系统」Arduino板,仅仅消耗0.36mA (360μA)。

ATMega328微控器具有六种睡眠模式,底下是依照「省电情况」排列的睡眠模式名称,以及Enerlib(註:Energy和Library,即:「能源」和「程式库」的缩写)程式库的五道函数指令对照表,排越后面越省电。「消耗电流」栏位指的是ATmega328处理器本身,而非整个控制板。
 

睡眠模式 Energy指令 中文直译 消耗电流
Idle Idle() 閒置 15mA
ADC Noise Reduction SleepADC() 类比数位转换器降低杂讯 6.5mA
Power-save PowerSave() 省电 1.62mA
Standby Standby() 待机 1.62mA
Extended Standby   延长待机 0.84mA
Power-down PowerDown() 断电 0.36mA



微控器内部除了中央处理器(CPU), 还有记忆体、类比数位转换器、序列通讯…等模组。越省电的模式,仍在运作中的模组就越少。

例如,在”Power-Down”(电源关闭)睡眠模式之下,微控器仅剩下外部中断和看门狗计时器(Watchdog Timer, 参阅下文说明)仍持续运作。而在Idle睡眠模式底下,SPI, UART(也就是序列埠)、计时器、类比数位转换器等,仍持续运作,只有中央处理器和快闪记忆体(Flash)时脉讯号被停止。

时脉讯号就像心跳一样,一旦停止时脉讯号,相关的元件也随之暂停。各种睡眠模式的详细说明,请参阅ATmega328微控器的资料手册,第39页,「Power Management and Sleep Modes(电源管理与睡眠模式)」单元。

採用Enerlib程式庫設定睡眠模式

Enerlib程式库可简化Arduino睡眠模式的程式设定,请先下载Enerlib程式库并解压缩到Arduino的libraries资料夹:
 

本实验程式的行为如下:
 

  • 启动时,每隔0.5秒点、灭三次位于第13脚的LED。
  •     LED闪烁完毕后,进入“Power-down(断电)”睡眠模式。
  •     当中断0(第2脚)的讯号改变时,唤醒Arduino,再次闪烁LED三次,接著再进入睡眠模式。



请先把Arduino的数位脚2接高电位(5V或3.3V插座):

 

反覆闪烁LED的基本程式码如下:
 

void setup() {

  Serial.begin(9600);

 
  pinMode(ledPin, OUTPUT);
  Serial.println("Running...");

}

 
void loop() {

  digitalWrite(ledPin, !digitalRead(ledPin));

  delay(500);

}



负责闪烁LED的关键叙述是这一行:

 

设定唤醒Arduino的中断服务常式

修改上一节的程式,建立Energy程式物件,并加入中断服务常式叙述(广告一下,中断服务常式的说明,请参阅《超图解Arduino互动设计入门》的D-3页):
 

若Arduino处于睡眠状态,只要中断0脚位的讯号改变,它就会被唤醒。然而,同一个程式其他叙述,也有可能需要接收中断0的讯息。为此,Energy提供一个用于判断Arduino是否处于睡眠状态的WasSleep()函数,若是,它将传回true。

底下是修改后的wakeISR中断处理常式,若Arduino之前处于睡眠状态,则state变数值将是1,若是在执行过程发生中断讯号,state值将是2:

 

透过state值,主程式将能得知中断的触发时机。补充说明,WasSleep()函数只能写在中断处理常式裡面。

让Arduino睡眠的主程式

主程式迴圈如下,它将在闪烁LED三次后进入最省电的「断电」睡眠模式:

 

完整的范例程式码如下:
 

#include <Enerlib.h>

Energy energy;             // 宣告"Energy"程式物件

const byte swPin = 2;      // 開關腳位
const byte ledPin = 13;    // LED腳位
byte times = 0;            // 記錄執行次數
volatile byte state = 0;   // 暫存執行狀態

void wakeISR() {
   if (energy.WasSleeping()) {
    state = 1;
  } else {
    state = 2;
  }
}

void setup() {
  Serial.begin(9600);
 
  pinMode(ledPin, OUTPUT);
  pinMode(swPin, INPUT);
  digitalWrite(swPin, HIGH);
 
  attachInterrupt(0, wakeISR, CHANGE);  // 附加中斷服務常式
  
  Serial.println("Running...");
}

void loop()
{
  if (state == 1) {
    Serial.println("Was sleeping...");
  } else if (state == 2) {
    Serial.println("Was awake...");
  }
  state = 0;
  
  digitalWrite(ledPin, !digitalRead(ledPin));
  delay(500);
  times ++;
  Serial.println(times);

  if (times > 5) {
    times = 0;
    Serial.println("Go to sleep...");
    energy.PowerDown();
  }
}



编译并上传程式到Arduino板之后,开启「序列埠监控视窗」,它将显示:

 

接著,把连接中断0的导线接到低电位(GND):

 

Arduino将被唤醒,并再次闪烁LED;笔者在LED闪烁的过程中,反覆将中断0接高、低电位,「序列埠监控视窗」因而呈现如下的内容:
 

看门狗计时器简介

看门狗计时器(Watchdog Timer, 简称WDT)是微控器内部的「当机」监控器,若微控器当掉了,它会自动重新启动微控器。其运作原理是,看门狗内部有个计时器,微处理器必须每隔一段时间,向看门狗发出一个讯号,重设计时器值。

 



若看门狗迟迟没有收到微处理器的讯号,计时器仍将继续倒数,直到计时值变成零,它就会认定微处理器已经当掉了,进而重新启动微处理器。

主程式可设定看门狗的计时器值,最短16ms,最长8s。Donal Morrissey写了一篇看门狗程式的介绍,以及沉睡8秒之后,切换LED状态的范例:Sleeping Arduino – Part 5 Wake Up Via The Watchdog Timer

[转]基于esp8266的开发导航帖arduino ide for esp8266

自从乐鑫esp8266出现后,其价格低廉的解决方案,引起了业界的广泛关注,堪称业界里程碑。但是很多人买来之后不知道怎么使用,今天在这里和大家讨论分享一下:   

   ESP8266简介,乐鑫的单芯片wifi 控制器

目前市面上有3种开发方式:
1:使用乐鑫AT 固件,使用时,单片机通过串口用AT指令来实现wifi连接、网络通迅,例如arduino uno+esp8266他们间使用AT指令来交互。

2:使用乐鑫SDK开发包,直接用C编程。例如果云esp8266 sdk开发之类。直接开发BIN文件
3:使用NODEMCU固件,这是国内大能 在SDK基础上,加入了LUA语言

现在,出现了第4种开发方式,arduino直接编程,原理还是在SDK基础上开发,不过已经封装为我们熟的Arduino语言了。(想想都有点小激动{:soso_e102:})

  注意已经不需要ARDUINO硬件了,而是利用arduino软件平台来编译8266的源码。即利用arduino 语句开发8266的sdk,

下面的几篇帖子将分别介绍开发的一些教程。

第一篇,基于esp8266的智能家居控制系统-基础篇1介绍arduino ide for esp8266,介绍背景,及软件下载

第二篇,基于esp8266的智能家居控制系统-基础篇2硬件平台搭建 ,搭建硬件开发环境
第三篇,基于esp8266的智能家居控制系统-通用篇,常用的示例代码,非无线
第四篇,基于esp8266的智能家居控制系统-局域网篇1 TCP/UDP,tcp通讯的准备作为客户端与电脑通讯
第五篇,基于esp8266的智能家居控制系统-局域网篇1手机控制和透传作为客户端与手机通讯及透传
第六篇,基于esp8266的智能家居控制系统-局域网篇2,tcpserver,作为服务端与其他作为客户端的设备通讯
楼楼将持续更新,么么哒

转载自:http://www.arduino.cn/forum.php?mod=viewthread&tid=18361&highlight=esp8266

用Arduino制作简易电压测试仪

一直想用锂电给arduino的板子供电,买了2种带充放的模块,其中一种有电量指示灯,另外一种没有,想在锂电为板子供电同时测量锂电电压,类似手机剩余电量显示这种。

电路比较简单,找2个10K的电阻串联在电池两端,电池负极接GND,电阻连接处接A3,代码如下:

#define voltsInPin A3

void setup()
{
  pinMode(voltsInPin, INPUT);

  Serial.begin(9600);
  Serial.println("volts:");
}

void loop()
{
  int rawReading = analogRead(voltsInPin);
  float volts = rawReading / 220.62 * 2;
  Serial.println(volts);
  delay(500);
}

每隔半秒测试一次,220.62是我根据万用表测出的数值经过换算得来的,理论上应该是(1023/5=204.8),乘以2是因为两个10K的电阻分压了,所以测量到的电压是实际的一半大小,3.7V锂电充满一般4.2V左右。然后我找了个4位计时器(TM1637),加了几句代码,可以用数码管显示电压值了,两个脚分别接D2和D3。

#define voltsInPin A3

// 下面是4位计时器定义
#include "TM1637.h"
#define CLK 3//pins definitions for TM1637 and can be changed to other ports       
#define DIO 2
TM1637 tm1637(CLK, DIO);

void setup()
{
  pinMode(voltsInPin, INPUT);
  //pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("volts:");

  tm1637.init();
  tm1637.set(BRIGHT_TYPICAL);

}

void loop()
{
  int rawReading = analogRead(voltsInPin);
  float volts = rawReading / 220.62 * 2;
  Serial.println(volts);
  int num = volts*100;
  // 4位数显示
  tm1637.display(0, num / 1000);
  tm1637.display(1, (num % 1000) / 100);
  tm1637.display(2, (num % 100) / 10);
  tm1637.display(3, num % 10);
  delay(500);
}

最后,请大家保持冷静,上面的代码在用USB供电时一点问题都没有,但是直接用升压模块接锂电供电后,测量的电压低了很多,而且跳跃的很厉害,我暂时还没有搞清楚到底是不能这样测试还是数码管的问题(数码管噪音很大),改天再测试了,或者还是直接用带电压显示的升压模块算了。。。。。。

人肉分析空调红外遥控器 with Arduino & C#(01)——温度篇

最近迷上了Arduino,简直痴迷,感叹小时候咋就没有那么好玩的东西。

那么,今天的主题是用Arduino控制红外发射器控制带红外的电器(没有红外的以后可以考虑另加,比如通过Nano板子:),今天的小白鼠是家里一个老的AUX壁挂空调,据说海尔的加密云云,改天再研究。

拿出UNO的板子,红外发送和接收的模块,装好库(https://github.com/z3t0/Arduino-IRremote ),翻翻示例,先是找到了IRrecvDumpV2,数据针脚插11,按下遥控器按钮后提示(IR code too long. Edit IRremoteInt.h and increase RAWBUF),问了万能的度娘,说是很多空调编码过长,100个缓存不够用,然调到255并无效果,此法弃疗。

接着问度娘,找到了一片神作,《“高帅富”空调的红外解码和遥控方法”》 http://www.arduino.cn/thread-5775-1-1.html,光看这个名字就厉害的不得了,我就转个代码,具体文章请去原地址拜读

/*
Author: AnalysIR
Revision: 1.0
 
This code is provided to overcome an issue with Arduino IR libraries
It allows you to capture raw timings for signals longer than 255 marks & spaces.
Typical use case is for long Air conditioner signals.
 
You can use the output to plug back into IRremote, to resend the signal.
 
This Software was written by AnalysIR.
 
Usage: Free to use, subject to conditions posted on blog below.
Please credit AnalysIR and provide a link to our website/blog, where possible.
 
Copyright AnalysIR 2014
 
Please refer to the blog posting for conditions associated with use.
[url]http://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino/[/url]
 
Connections:
IR Receiver      Arduino
V+          ->  +5v
GND          ->  GND
Signal Out   ->  Digital Pin 2
(If using a 3V Arduino, you may connect V+ to +3V)
*/
 
#define LEDPIN 13
//you may increase this value on Arduinos with greater than 2k SRAM
#define maxLen 600
 
volatile  unsigned int irBuffer[maxLen]; //stores timings - volatile because changed by ISR
volatile unsigned int x = 0; //Pointer thru irBuffer - volatile because changed by ISR
 
void setup() {
  Serial.begin(9600); //change BAUD rate as required
  attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);//set up ISR for receiving IR signal
}
 
void loop() {
  // put your main code here, to run repeatedly:
 
  //Serial.println(F("Press the button on the remote now - once only"));
  delay(5000); // pause 5 secs
  if (x) { //if a signal is captured
    digitalWrite(LEDPIN, HIGH);//visual indicator that signal received
    Serial.println();
    Serial.print(F("Raw: (")); //dump raw header format - for library
    Serial.print((x - 1));
    Serial.print(F(") "));
    detachInterrupt(0);//stop interrupts & capture until finshed here
    for (int i = 1; i < x; i++) { //now dump the times
      //if (!(i & 0x1)) Serial.print(F("-"));
      Serial.print(irBuffer[i] - irBuffer[i - 1]);
      Serial.print(F(","));
    }
    x = 0;
    Serial.println();
    Serial.println();
    digitalWrite(LEDPIN, LOW);//end of visual indicator, for this time
    attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);//re-enable ISR for receiving IR signal
  }
 
}
 
void rxIR_Interrupt_Handler() {
  if (x > maxLen) return; //ignore if irBuffer is already full
  irBuffer[x++] = micros(); //just continually record the time-stamp of signal transitions
 
}

仔细通篇看完,用此君的代码,读到了211和423,这里遇到了一个坑,其实此君代码会把连按的2次操作合到一起,最后经过多次测试,这只AUX遥控的按键RAW统统是211长度。经过对IRremote库的源代码的涩读(ir_NEC.cpp),发现了这个基本符合NEC代码的开头,先来段读出来的RAW:

9016,4520,540,1684,544,1684,544,560,544,556,544,556,544,560,544,1684,544,1684,544,1684,548,1680,548,1684,544,556,544,556,544,560,544,1684,544,1684,544,560,544,556,544,556,544,560,544,556,544,556,544,560,544,556,544,556,544,560,544,556,544,556,544,560,544,556,544,556,544,556,548,556,544,556,544,560,544,556,544,556,544,1688,544,556,544,1684,544,556,544,560,544,556,544,556,544,560,544,556,544,556,548,556,544,556,544,556,548,556,564,536,544,556,548,556,544,1684,568,532,568,532,568,536,564,540,540,560,560,540,564,540,564,536,564,536,544,556,568,532,548,556,568,532,568,532,568,536,568,532,564,540,564,536,568,532,568,532,568,536,568,532,568,532,568,536,568,532,564,540,568,532,568,532,568,532,572,532,568,532,568,536,568,532,568,1660,568,532,568,1664,564,536,568,532,568,536,564,536,568,532,568,1664,568,1660,568,1660,568,1660,568,536,568,1660,568,1660,568,532,596

看花了吧,然后用IRsendRawDemo竟然可以成功实现功能,但arduino那么小的内存如何装得下所有的代码,就算装下了,这不分析下具体原理和代码生成方式晚上如何睡得着。

于是,摘下眼镜揉一揉接着戴上,要上了!

第一个9000左右和第二个4500左右的值代表了NEC的特征,后面209个的最后一个600左右的是结尾,那么中间剩下208个就好分析了(什么,分析无力,那么多数字看花了,问我什么意思我也不知道,于是乎又去找度娘姐姐研究了下红外的原理:【扫盲贴】浅谈38K红外发射接受编码(非常好) http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=21227800&id=5747884 一遍看不懂多看几遍,还是看不懂就往下看,那个数字代表高电平低电平的时长,但是用38KHz波来实现的,有这个波低电平,没这个波高电平,一低一高的组合可以变成0或1),于是乎又从库的源代码中找到了转换的方法(ir_NEC.cpp)就2个方法,其中一个就是解码:

  // Build the data
  for (int i = 0;  i < NEC_BITS;  i++) {
    // Check data "mark"
    if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK))  return false ;
    offset++;
    // Suppend this bit
    if  (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE ))  data = (data << 1) | 1 ;
    else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE))  data = (data << 1) | 0 ;
    else                return false ;
    offset++;
  }

先来560左右的NEC_BIT_MARK,然后判断是NEC_ONE_SPACE就是1,NEC_ZERO_SPACE就是0,转换如下:

11000011111000110000000000000000000001010000000000000010000000000000000000000000000000001010000011110110

共104bit,换算成16进制就是C3E3000005000200000000A0F6(此时按的32度,除湿,微风,关机)

此时你心中也许会有千万匹神兽飞奔而过,那么多数字眼睛也看花了,老兄,你看看标题好不好,C#写的转换代码啦,你还真以为全人肉解码啊!

同理下面是开机代码:

11000011111000110000000000000000000001010000000000000010000000000000000000000100000000001010000011110001

共104bit C3E3000005000200000400A0F1(32度,除湿,微风,开机)

28度 除湿 风速无,开

11000011111001010000000000000000000001010000000000000010000000000000000000000100000000001010000011110110 
104bit C3E5000005000200000400A0F6

28度 除湿 风速无,关

11000011111001010000000000000000000001010000000000000010000000000000000000000000000000001010000011110010 
104bit C3E5000005000200000000A0F2

27度 除湿 风速无,开

11000011111110010000000000000000000001010000000000000010000000000000000000000100000000001010000011100110 
104bit C3F9000005000200000400A0E6

27度 除湿 风速无,关

11000011111110010000000000000000000001010000000000000010000000000000000000000000000000001010000011100010 
104bit C3F9000005000200000000A0E2

好,下面就找到了变化的部分,C3之后和结尾,结尾估计是校验码,以后再说,C3之后1个字节估计就和温度有关了,经过人肉演算,发现了如下规律:

E3 11100011 32

……

E5 11100101 28

F9 11111001 27

E9 11101001 26

F1 11110001 25

E1 11100001 24

FE 11111110 23

……

E2 11100010 16

什么,你没有找到规律!从第8位倒着往前看5位,还是不明白?我又抄了一遍。。。。。。

11000 32(度)

……

10100 28

10011 27

10010 26

10001 25

10000 24

01111 23

……

01000 16度(8)

基本上就是这样了,温度应该就是这几位了,顺便补充一下,大多数空调遥控器会把所有的状态一起发过去,所以每次一样长,而且带有所有信息,今天就分析到这,下次继续。

下面放一些也许值得参考的文章(次序不分先后):

万用遥控之红外解码分析仪(上位机源码、下位机源码、详细的制作讲解)

【扫盲贴】浅谈38K红外发射接受编码(非常好)

C#+Arduino使用红外遥控器

“高帅富”空调的红外解码和遥控方法【精华】 

Arduino教程(提高篇)——红外遥控(接收篇)

C#实现读取红外线遥控器

Arduino-IRremote库

海尔彩电遥控器型号对照表