├── README.md ├── arduino ├── deco_lights │ └── deco_lights.ino ├── morse_blinker │ └── morse_blinker.ino └── promini_test │ └── promini_test.ino └── nodemcu ├── display-tricks ├── disp_gr_test.lua ├── display_lowlev.lua ├── display_test.lua ├── hello.bit ├── rotenc.lua ├── scull.bit ├── scullcandy.lua └── xbm2bit.pl ├── nightlight ├── .gitignore ├── README.md ├── config.default.lua ├── ds18b20.lua ├── init.lua └── nightlight.lua └── smartclock.lua /README.md: -------------------------------------------------------------------------------- 1 | # IoT fun 2 | Nothing special here, just tracking my personal experiments in IoT (ESP8266, NodeMCU, etc). 3 | 4 | You may wish to take a look at https://github.com/smartynov/nodemcu-modules where I publish my ready to use modules for NodeMCU. 5 | 6 | Should you have any questions, please concact me at email sergey@martynov.info, facebook or linkedin. 7 | -------------------------------------------------------------------------------- /arduino/deco_lights/deco_lights.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // set to pin connected to data input of WS8212 (NeoPixel) strip 4 | #define PIN 0 5 | 6 | // any pin with analog input (used to initialize random number generator) 7 | #define RNDPIN 2 8 | 9 | // number of LEDs (NeoPixels) in your strip 10 | // (please note that you need 3 bytes of RAM available for each pixel) 11 | #define NUMPIXELS 60 12 | 13 | // max LED brightness (1 to 255) – start with low values! 14 | // (please note that high brightness requires a LOT of power) 15 | #define BRIGHTNESS 50 16 | 17 | // increase to get narrow spots, decrease to get wider spots 18 | #define FOCUS 65 19 | 20 | // decrease to speed up, increase to slow down (it's not a delay actually) 21 | #define DELAY 5000 22 | 23 | // set to 1 to display FPS rate 24 | #define DEBUG 0 25 | 26 | // if true, wrap color wave over the edge (used for circular stripes) 27 | #define WRAP 1 28 | 29 | 30 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); 31 | 32 | // we have 3 color spots (reg, green, blue) oscillating along the strip with different speeds 33 | float spdr, spdg, spdb; 34 | float offset; 35 | 36 | #if DEBUG 37 | // track fps rate 38 | long nextms = 0; 39 | int pfps = 0, fps = 0; 40 | #endif 41 | 42 | // the real exponent function is too slow, so I created an approximation (only for x < 0) 43 | float myexp(float x) { 44 | return (1.0/(1.0-(0.634-1.344*x)*x)); 45 | } 46 | 47 | 48 | void setup() { 49 | // initialize pseudo-random number generator with some random value 50 | randomSeed(analogRead(RNDPIN)); 51 | 52 | // assign random speed to each spot 53 | spdr = 1.0 + random(200) / 100.0; 54 | spdg = 1.0 + random(200) / 100.0; 55 | spdb = 1.0 + random(200) / 100.0; 56 | 57 | // set random offset so spots start in random locations 58 | offset = random(10000) / 100.0; 59 | 60 | // initialize LED strip 61 | strip.begin(); 62 | strip.show(); 63 | } 64 | 65 | void loop() { 66 | // use real time to recalculate position of each color spot 67 | long ms = millis(); 68 | // scale time to float value 69 | float m = offset + (float)ms/DELAY; 70 | // add some non-linearity 71 | m = m - 42.5*cos(m/552.0) - 6.5*cos(m/142.0); 72 | 73 | // recalculate position of each spot (measured on a scale of 0 to 1) 74 | float posr = 0.15 + 0.55*sin(m*spdr); 75 | float posg = 0.5 + 0.65*sin(m*spdg); 76 | float posb = 0.85 + 0.75*sin(m*spdb); 77 | 78 | // now iterate over each pixel and calculate it's color 79 | for (int i=0; inextms) { 105 | // 1 second passed – reset counter 106 | nextms = ms + 1000; 107 | pfps = fps; 108 | fps = 0; 109 | } 110 | // show FPS rate by setting one pixel to white 111 | strip.setPixelColor(pfps,BRIGHTNESS,BRIGHTNESS,BRIGHTNESS); 112 | #endif 113 | 114 | // send data to LED strip 115 | strip.show(); 116 | } 117 | 118 | 119 | -------------------------------------------------------------------------------- /arduino/morse_blinker/morse_blinker.ino: -------------------------------------------------------------------------------- 1 | #define PIN_LED 1 2 | #define PIN_SPEAKER 1 3 | 4 | #include 5 | 6 | //LEDMorseSender sender(PIN_LED); 7 | SpeakerMorseSender speaker( 8 | PIN_SPEAKER, 9 | 880, // tone frequency 10 | -1, // carrier frequency 11 | 14); // wpm 12 | 13 | void setup() { 14 | /* 15 | sender.setup(); 16 | // sender.setMessage(String(F("lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."))); 17 | sender.setMessage(msg); 18 | // sender.setMessage(String("lorem ipsum dolor sit amet ")); 19 | // sender.setMessage(String("i love you ")); 20 | sender.startSending(); 21 | */ 22 | speaker.setup(); 23 | speaker.setMessage(String("lorem ipsum dolor sit amet, consectetur adipiscing elit")); 24 | speaker.startSending(); 25 | } 26 | 27 | void loop() { 28 | /* if (!sender.continueSending()) 29 | { 30 | delay(1000); 31 | sender.startSending(); 32 | } 33 | */ 34 | if (!speaker.continueSending()) 35 | { 36 | delay(4000); 37 | speaker.startSending(); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /arduino/promini_test/promini_test.ino: -------------------------------------------------------------------------------- 1 | 2 | #define PIN 13 3 | 4 | // the setup function runs once when you press reset or power the board 5 | void setup() { 6 | // initialize digital pin 13 as an output. 7 | pinMode(PIN, OUTPUT); 8 | Serial.begin(9600); 9 | } 10 | 11 | float vsmooth = 0; 12 | 13 | #define SMOOTH_FACTOR 0.9 14 | 15 | // the loop function runs over and over again forever 16 | void loop() { 17 | digitalWrite(PIN, HIGH); // turn the LED on (HIGH is the voltage level) 18 | delay(1000); // wait for a second 19 | digitalWrite(PIN, LOW); // turn the LED off by making the voltage LOW 20 | delay(100); // wait for a second 21 | int sensorValue = analogRead(A0); 22 | float voltage = sensorValue * (5.0 / 1023.0); 23 | vsmooth = vsmooth * SMOOTH_FACTOR + voltage * (1.0 - SMOOTH_FACTOR); 24 | Serial.println(vsmooth); 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /nodemcu/display-tricks/disp_gr_test.lua: -------------------------------------------------------------------------------- 1 | 2 | -- setup I2c and connect display 3 | function init_i2c_display() 4 | -- SDA and SCL can be assigned freely to available GPIOs 5 | local sda = 5 -- GPIO14 6 | local scl = 6 -- GPIO12 7 | local sla = 0x3c 8 | i2c.setup(0, sda, scl, i2c.SLOW) 9 | disp = u8g.ssd1306_128x64_i2c(sla) 10 | end 11 | 12 | 13 | -- graphic test components 14 | function prepare() 15 | disp:setFont(u8g.font_6x10) 16 | disp:setFontRefHeightExtendedText() 17 | disp:setDefaultForegroundColor() 18 | disp:setFontPosTop() 19 | end 20 | 21 | function box_frame(a) 22 | disp:drawStr(0, 0, "drawBox") 23 | disp:drawBox(5, 10, 20, 10) 24 | disp:drawBox(10+a, 15, 30, 7) 25 | disp:drawStr(0, 30, "drawFrame") 26 | disp:drawFrame(5, 10+30, 20, 10) 27 | disp:drawFrame(10+a, 15+30, 30, 7) 28 | end 29 | 30 | function disc_circle(a) 31 | disp:drawStr(0, 0, "drawDisc") 32 | disp:drawDisc(10, 18, 9) 33 | disp:drawDisc(24+a, 16, 7) 34 | disp:drawStr(0, 30, "drawCircle") 35 | disp:drawCircle(10, 18+30, 9) 36 | disp:drawCircle(24+a, 16+30, 7) 37 | end 38 | 39 | function r_frame(a) 40 | disp:drawStr(0, 0, "drawRFrame/Box") 41 | disp:drawRFrame(5, 10, 40, 30, a+1) 42 | disp:drawRBox(50, 10, 25, 40, a+1) 43 | end 44 | 45 | function stringtest(a) 46 | disp:drawStr(30+a, 31, " 0") 47 | disp:drawStr90(30, 31+a, " 90") 48 | disp:drawStr180(30-a, 31, " 180") 49 | disp:drawStr270(30, 31-a, " 270") 50 | end 51 | 52 | function line(a) 53 | disp:drawStr(0, 0, "drawLine") 54 | disp:drawLine(7+a, 10, 40, 55) 55 | disp:drawLine(7+a*2, 10, 60, 55) 56 | disp:drawLine(7+a*3, 10, 80, 55) 57 | disp:drawLine(7+a*4, 10, 100, 55) 58 | end 59 | 60 | function triangle(a) 61 | local offset = a 62 | disp:drawStr(0, 0, "drawTriangle") 63 | disp:drawTriangle(14,7, 45,30, 10,40) 64 | disp:drawTriangle(14+offset,7-offset, 45+offset,30-offset, 57+offset,10-offset) 65 | disp:drawTriangle(57+offset*2,10, 45+offset*2,30, 86+offset*2,53) 66 | disp:drawTriangle(10+offset,40+offset, 45+offset,30+offset, 86+offset,53+offset) 67 | end 68 | 69 | function ascii_1() 70 | local x, y, s 71 | disp:drawStr(0, 0, "ASCII page 1") 72 | for y = 0, 5, 1 do 73 | for x = 0, 15, 1 do 74 | s = y*16 + x + 32 75 | disp:drawStr(x*7, y*10+10, string.char(s)) 76 | end 77 | end 78 | end 79 | 80 | function extra_page(a) 81 | disp:drawStr(0, 12, "setScale2x2") 82 | disp:setScale2x2() 83 | disp:drawStr(0, 6+a, "setScale2x2") 84 | disp:undoScale() 85 | end 86 | 87 | 88 | -- the draw() routine 89 | function draw(draw_state) 90 | local component = bit.rshift(draw_state, 5) 91 | 92 | prepare() 93 | 94 | if (component == 0) then 95 | box_frame(bit.band(draw_state, 31)) 96 | elseif (component == 1) then 97 | disc_circle(bit.band(draw_state, 31)) 98 | elseif (component == 2) then 99 | r_frame(bit.band(draw_state, 31)) 100 | elseif (component == 3) then 101 | stringtest(bit.band(draw_state, 31)) 102 | elseif (component == 4) then 103 | line(bit.band(draw_state, 31)) 104 | elseif (component == 5) then 105 | triangle(bit.band(draw_state, 31)) 106 | elseif (component == 6) then 107 | ascii_1() 108 | elseif (component == 7) then 109 | extra_page(bit.band(draw_state, 31)) 110 | end 111 | end 112 | 113 | function graphics_test(delay) 114 | print("--- Starting Graphics Test ---") 115 | 116 | -- cycle through all components 117 | local draw_state 118 | for draw_state = 0, 7 + 4*8*8, 1 do 119 | disp:firstPage() 120 | repeat 121 | draw(draw_state) 122 | until disp:nextPage() == false 123 | --print(node.heap()) 124 | tmr.delay(delay) 125 | -- re-trigger Watchdog! 126 | tmr.wdclr() 127 | end 128 | 129 | print("--- Graphics Test done ---") 130 | end 131 | 132 | init_i2c_display() 133 | graphics_test(10000) 134 | -------------------------------------------------------------------------------- /nodemcu/display-tricks/display_lowlev.lua: -------------------------------------------------------------------------------- 1 | sda = 3 2 | scl = 2 3 | sla = 0x3c 4 | 5 | 6 | function plot(x, y, z) 7 | ln = x % 16 8 | hn = x/16 9 | pg = y/8 10 | bt = 2 ^ (y % 8) 11 | command(0x00 + ln, 0x10 + hn, 0xb0 + pg) 12 | if (z==0) then 13 | if(x~=63) then bt=0 else bt=0x99 end 14 | end 15 | if(pg == 0) then bt = bit.bor(bt,1) end 16 | if(pg == 7) then bt = bit.bor(bt,128) end 17 | data(bt) 18 | end 19 | 20 | function data(...) 21 | i2c.start(0) 22 | i2c.address(0, sla, i2c.TRANSMITTER) 23 | i2c.write(0, 0x40, arg) 24 | i2c.stop(0) 25 | end 26 | 27 | function command(...) 28 | i2c.start(0) 29 | i2c.address(0, sla, i2c.TRANSMITTER) 30 | i2c.write(0, 0, arg) 31 | i2c.stop(0) 32 | end 33 | 34 | function oled_clear() 35 | command(0x20, 0x01) 36 | command(0xB0, 0x00, 0x10) -- home 37 | command(0x21, 0x00, 0x7f) 38 | for i=0,127 do 39 | command(0x21, 0x00 + i, 0x7f) 40 | data(1,2,3,4,5,6,7,0) 41 | end 42 | command(0x20, 0x02) -- page addressing mode 43 | end 44 | 45 | function oled_init() 46 | i2c.setup(0, sda, scl, i2c.SLOW) 47 | command(0x8d, 0x14) -- enable charge pump 48 | command(0xaf) -- display on, resume to RAM 49 | command(0xd3, 0x00) -- set vertical shift to 0 50 | command(0x40) -- set display start line to 0 51 | command(0xa1) -- column 127 is mapped to SEG0 52 | command(0xc8) -- remapped mode 53 | command(0xda, 0x12) -- alternative COM pin configuration, disable left/right remap 54 | command(0x81, 0xff) -- set contrast to 255 55 | command(0x20, 0x02) -- page addressing mode 56 | end 57 | 58 | oled_init() 59 | oled_clear() 60 | 61 | --command(0xaf) -- on 62 | command(0x26, 0x00, 0x01, 0x04, 0x03, 0x15, 0x6f) -- scroll 63 | command(0x26, 0x00, 0x00, 0x06, 0x07, 0x00, 0xff) -- scroll all 64 | command(0x2f) -- scroll on 65 | command(0x2e) -- scroll off 66 | 67 | command(0xda, 0x12) 68 | 69 | for i=8,55 do 70 | plot(i,i,1) 71 | plot(126-i,i,1) 72 | tmr.wdclr() 73 | end 74 | -------------------------------------------------------------------------------- /nodemcu/display-tricks/display_test.lua: -------------------------------------------------------------------------------- 1 | local sda = 3 2 | local scl = 2 3 | local sla = 0x3c 4 | i2c.setup(0, sda, scl, i2c.SLOW) 5 | disp = u8g.ssd1306_128x64_i2c(sla) 6 | 7 | file.open("hello.bit","r") 8 | local background = file.read(128*64/8) 9 | 10 | 11 | --disp:setFontRefHeightExtendedText() 12 | --disp:setDefaultForegroundColor() 13 | --disp:setFontPosTop() 14 | disp:firstPage() 15 | repeat 16 | 17 | --disp:drawCircle(40, 30, 20) 18 | --disp:setFont(u8g.font_6x10) 19 | --disp:drawStr(70, 50, "Test 123") 20 | --disp:setFont(u8g.font_chikita) 21 | --disp:drawStr(30, 30, "86!") 22 | 23 | -- u8g.setFont(u8g_font_unifont); 24 | -- u8g.drawStr( 0, 20, "Hello World!"); 25 | 26 | disp:drawBitmap(0, 0, 128/8, 64, test1) 27 | until disp:nextPage() == false 28 | -------------------------------------------------------------------------------- /nodemcu/display-tricks/hello.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartynov/iotfun/5e677ffd440e9c03b12e4f6338174291faeed9d0/nodemcu/display-tricks/hello.bit -------------------------------------------------------------------------------- /nodemcu/display-tricks/rotenc.lua: -------------------------------------------------------------------------------- 1 | local sda = 3 2 | local scl = 2 3 | local sla = 0x3c 4 | i2c.setup(0, sda, scl, i2c.SLOW) 5 | local disp = u8g.ssd1306_128x64_i2c(sla) 6 | 7 | file.open("scull.bit","r") 8 | local scull = file.read(16*16/8) 9 | file.close() 10 | 11 | function psin(idx) 12 | idx = idx % 128 13 | lookUp = 32 - idx % 64 14 | val = 256 - (lookUp * lookUp) / 4 15 | if (idx > 64) then 16 | val = - val; 17 | end 18 | return 256+val 19 | end 20 | 21 | local psw = 5 22 | local pdt = 6 23 | local pcl = 7 24 | 25 | gpio.mode(psw, gpio.INPUT) 26 | gpio.mode(pdt, gpio.INPUT) 27 | gpio.mode(pcl, gpio.INPUT) 28 | 29 | local prev = 0 30 | 31 | local value = 100 32 | local prev_value = value 33 | 34 | tmr.stop(1) 35 | tmr.alarm(1,1,1,function() 36 | local sw = gpio.read(psw) == 0 37 | local dt = gpio.read(pdt) 38 | local cl = gpio.read(pcl) 39 | if (cl == 1) then cur = 3 - dt else cur = dt end 40 | local dir = cur - prev 41 | if not(dir == 0) then dir = dir % 4 - 2 end 42 | prev = cur 43 | 44 | value = value + dir 45 | if not(prev_value == value) and cl == dt then 46 | disp:firstPage() 47 | repeat 48 | local x = value % (128+16) - 16 49 | local y = 20 -- + psin(4*x) / 32 50 | disp:drawBitmap(x, y, 16/8, 16, scull) 51 | -- if (sw) then disp:drawDisc(value % 127, 40, 7) 52 | -- else disp:drawCircle(value % 127, 40, 7) end 53 | tmr.wdclr() 54 | until disp:nextPage() == false 55 | end 56 | prev_value = value 57 | --print ("sw="..sw.."; dt="..dt.."; cl="..cl..";") 58 | --]] 59 | tmr.wdclr() 60 | end) 61 | -------------------------------------------------------------------------------- /nodemcu/display-tricks/scull.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartynov/iotfun/5e677ffd440e9c03b12e4f6338174291faeed9d0/nodemcu/display-tricks/scull.bit -------------------------------------------------------------------------------- /nodemcu/display-tricks/scullcandy.lua: -------------------------------------------------------------------------------- 1 | local sda = 3 2 | local scl = 2 3 | local sla = 0x3c 4 | i2c.setup(0, sda, scl, i2c.SLOW) 5 | disp = u8g.ssd1306_128x64_i2c(sla) 6 | 7 | file.open("hello.bit","r") 8 | local hello = file.read(128*64/8) 9 | file.close() 10 | file.open("scull.bit","r") 11 | local scull = file.read(16*16/8) 12 | file.close() 13 | 14 | function psin(idx) 15 | idx = idx % 128 16 | lookUp = 32 - idx % 64 17 | val = 256 - (lookUp * lookUp) / 4 18 | if (idx > 64) then 19 | val = - val; 20 | end 21 | return 256+val 22 | end 23 | 24 | local x = 0 25 | local y = 0 26 | 27 | for i=0,100 do 28 | disp:firstPage() 29 | repeat 30 | if i < 40 then 31 | disp:drawBitmap(0, 0, 128/8, 64, hello) 32 | end 33 | disp:drawBitmap(x, y, 16/8, 16, scull) 34 | for s = 1,64 do 35 | disp:drawLine(2*(s-1), psin(4*(s-1)) / 16, 2*s, psin(4*s) / 16) 36 | end 37 | until disp:nextPage() == false 38 | x = (x + 1) % (128 - 16) 39 | y = (y + 1) % (64 - 16) 40 | tmr.wdclr() 41 | end 42 | -------------------------------------------------------------------------------- /nodemcu/display-tricks/xbm2bit.pl: -------------------------------------------------------------------------------- 1 | #!/opt/local/bin/perl 2 | use strict; 3 | undef $/; 4 | print pack "b*", unpack "B*", pack "H*", grep { s/^.*{|\s+|0x|,|}.*$//gs } <>; 5 | 6 | -------------------------------------------------------------------------------- /nodemcu/nightlight/.gitignore: -------------------------------------------------------------------------------- 1 | config.lua 2 | -------------------------------------------------------------------------------- /nodemcu/nightlight/README.md: -------------------------------------------------------------------------------- 1 | # NightLight 2 | 3 | A smart LED stripe controller that keep track of time and motion around and behaves in intelligent way. 4 | 5 | ## Boot process & OTA 6 | 7 | On boot ESP8266 connects to wifi and makes request to 8 | 9 | 1. Connect to wifi 10 | 2. Wait 0-5 seconds to get an IP 11 | 3. If no IP obtained in 5 seconds – go to step 7 12 | 4. Start telnet server (remote console) 13 | 5. TODO: Make request to http://iot.martynov.info/api/check?mac=MAC_ADDRESS&ip=LOCAL_IP&p=nightlight 14 | 6. TODO: Read response, if it contains "stop" – do nothing more 15 | 7. Run nightlight.lc 16 | -------------------------------------------------------------------------------- /nodemcu/nightlight/config.default.lua: -------------------------------------------------------------------------------- 1 | 2 | -- hardware setup 3 | PIN_PIR = 2 -- PIR sensor 4 | PIN_LED_WARM = 7 -- warm light 5 | PIN_LED_COLD = 5 -- cold light 6 | PIN_DS18B20 = 1 -- DS18B20 temperature sensor 7 | 8 | -- settings 9 | tzOffset = 0*3600 -- local time zone offset in seconds 10 | motionDelay = 90 -- seconds since last motion to start turning off 11 | 12 | -- local wifi settings 13 | WIFI_SSID = "" 14 | WIFI_PASSWORD = "" 15 | 16 | -- data.sparkfun.com (leave empty to disable) 17 | PUBLIC_KEY = "" 18 | PRIVATE_KEY = "" 19 | 20 | -- enable telnet (set to 0 to disable) 21 | TELNET_PORT = 21 22 | -------------------------------------------------------------------------------- /nodemcu/nightlight/ds18b20.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- DS18B20 one wire module for NODEMCU 3 | -- NODEMCU TEAM 4 | -- LICENCE: http://opensource.org/licenses/MIT 5 | -- Vowstar 6 | -- 2015/02/14 sza2 Fix for negative values 7 | -------------------------------------------------------------------------------- 8 | 9 | -- Set module name as parameter of require 10 | local modname = ... 11 | local M = {} 12 | _G[modname] = M 13 | -------------------------------------------------------------------------------- 14 | -- Local used variables 15 | -------------------------------------------------------------------------------- 16 | -- DS18B20 dq pin 17 | local pin = nil 18 | -------------------------------------------------------------------------------- 19 | -- Local used modules 20 | -------------------------------------------------------------------------------- 21 | -- Table module 22 | local table = table 23 | -- String module 24 | local string = string 25 | -- One wire module 26 | local ow = ow 27 | -- Timer module 28 | local tmr = tmr 29 | -- Limited to local environment 30 | setfenv(1,M) 31 | -------------------------------------------------------------------------------- 32 | -- Implementation 33 | -------------------------------------------------------------------------------- 34 | C = 0 35 | F = 1 36 | K = 2 37 | function setup(dq) 38 | pin = dq 39 | if(pin == nil) then 40 | return 41 | end 42 | ow.setup(pin) 43 | end 44 | 45 | function addrs() 46 | setup(pin) 47 | tbl = {} 48 | ow.reset_search(pin) 49 | repeat 50 | addr = ow.search(pin) 51 | if(addr ~= nil) then 52 | table.insert(tbl, addr) 53 | end 54 | tmr.wdclr() 55 | until (addr == nil) 56 | ow.reset_search(pin) 57 | return tbl 58 | end 59 | 60 | function readNumber(addr, unit) 61 | result = nil 62 | setup(pin) 63 | flag = false 64 | if(addr == nil) then 65 | ow.reset_search(pin) 66 | count = 0 67 | repeat 68 | count = count + 1 69 | addr = ow.search(pin) 70 | tmr.wdclr() 71 | until((addr ~= nil) or (count > 100)) 72 | ow.reset_search(pin) 73 | end 74 | if(addr == nil) then 75 | return result 76 | end 77 | crc = ow.crc8(string.sub(addr,1,7)) 78 | if (crc == addr:byte(8)) then 79 | if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then 80 | -- print("Device is a DS18S20 family device.") 81 | ow.reset(pin) 82 | ow.select(pin, addr) 83 | ow.write(pin, 0x44, 1) 84 | -- tmr.delay(1000000) 85 | present = ow.reset(pin) 86 | ow.select(pin, addr) 87 | ow.write(pin,0xBE,1) 88 | -- print("P="..present) 89 | data = nil 90 | data = string.char(ow.read(pin)) 91 | for i = 1, 8 do 92 | data = data .. string.char(ow.read(pin)) 93 | end 94 | -- print(data:byte(1,9)) 95 | crc = ow.crc8(string.sub(data,1,8)) 96 | -- print("CRC="..crc) 97 | if (crc == data:byte(9)) then 98 | t = (data:byte(1) + data:byte(2) * 256) 99 | if (t > 32767) then 100 | t = t - 65536 101 | end 102 | 103 | if (addr:byte(1) == 0x28) then 104 | t = t * 625 -- DS18B20, 4 fractional bits 105 | else 106 | t = t * 5000 -- DS18S20, 1 fractional bit 107 | end 108 | 109 | if(unit == nil or unit == 'C') then 110 | -- do nothing 111 | elseif(unit == 'F') then 112 | t = t * 18 / 10 + 320000 113 | elseif(unit == 'K') then 114 | t = t + 2731500 115 | else 116 | return nil 117 | end 118 | t = t / 1000 119 | return t 120 | end 121 | tmr.wdclr() 122 | else 123 | -- print("Device family is not recognized.") 124 | end 125 | else 126 | -- print("CRC is not valid!") 127 | end 128 | return result 129 | end 130 | 131 | function read(addr, unit) 132 | t = readNumber(addr, unit) 133 | if (t == nil) then 134 | return nil 135 | else 136 | return t 137 | end 138 | end 139 | 140 | -- Return module table 141 | return M 142 | -------------------------------------------------------------------------------- /nodemcu/nightlight/init.lua: -------------------------------------------------------------------------------- 1 | -- init 2 | dofile("config.lua") 3 | 4 | -- init pins 5 | gpio.mode(PIN_PIR, gpio.INPUT) 6 | gpio.mode(PIN_LED_WARM, gpio.OUTPUT) 7 | gpio.mode(PIN_LED_COLD, gpio.OUTPUT) 8 | gpio.write(PIN_LED_WARM, gpio.LOW) 9 | gpio.write(PIN_LED_COLD, gpio.LOW) 10 | gpio.mode(PIN_DS18B20, gpio.OUTPUT) 11 | gpio.write(PIN_DS18B20, gpio.HIGH) 12 | 13 | wifi.setmode(wifi.STATION) 14 | wifi.sta.config(WIFI_SSID, WIFI_PASSWORD) 15 | wifi.sleeptype(wifi.MODEM_SLEEP) 16 | 17 | uart.setup(0,115200,8,0,1) 18 | 19 | print("init") 20 | local i = 0 21 | local runNightLight = 1 22 | 23 | tmr.alarm(0, 500, tmr.ALARM_AUTO, function() 24 | i = i + 1 25 | if i == 1 then 26 | node.compile("nightlight.lua") 27 | end 28 | 29 | if TELNET_PORT > 0 then 30 | local ip = wifi.sta.getip() 31 | if ip == nil or ip == "0.0.0.0" then 32 | print("Waiting for IP...") 33 | else 34 | print("IP:" .. wifi.sta.getip()) 35 | telnet = require("telnet-server") 36 | telnet.start(TELNET_PORT) 37 | TELNET_PORT = 0 38 | end 39 | end 40 | 41 | if i > 5 and runNightLight > 0 then 42 | runNightLight = 0 43 | dofile("nightlight.lc") 44 | end 45 | 46 | if runNightLight == 0 and TELNET_PORT == 0 then 47 | tmr.stop(0) 48 | end 49 | 50 | collectgarbage() 51 | end) 52 | 53 | 54 | -------------------------------------------------------------------------------- /nodemcu/nightlight/nightlight.lua: -------------------------------------------------------------------------------- 1 | -- NightLight PIR sensor LED control 2 | 3 | -- settings 4 | local fadeInTime = 2700 -- ms to turn light on 5 | local fadeOutTime = 9100 -- ms to turn light off 6 | local fadeStep = 35 -- fade step in ms 7 | local watchStep = 470 -- PIR watch step in ms 8 | local sendStep = 60*1000 -- send data each ms 9 | 10 | -- constants 11 | local OFF, ON, FADEIN, FADEOUT = 0, 1, 2, 3 12 | 13 | -- vars 14 | local state = OFF 15 | local lastMotion = 0 16 | local curPower = 0 17 | local colorTemp = 0 -- 0 = warm, 1000 = cold 18 | local maxPower = 1000 19 | local nextTimeSync = 0 20 | local isPwm = 0 21 | local isRealTime = 0 22 | 23 | local temperature = 0 24 | local illuminance = 0 25 | 26 | --print("+nightlight") 27 | 28 | local http = require("http-client") 29 | 30 | local ds18b20 = require("ds18b20") 31 | ds18b20.setup(PIN_DS18B20) 32 | 33 | function setPower () 34 | --print("+setPower "..curPower) 35 | if curPower <= 0 then 36 | pwm.stop(PIN_LED_WARM) 37 | gpio.write(PIN_LED_WARM, gpio.LOW) 38 | pwm.stop(PIN_LED_COLD) 39 | gpio.write(PIN_LED_COLD, gpio.LOW) 40 | isPwm = 0 41 | else 42 | if isPwm == 0 then 43 | pwm.setup(PIN_LED_WARM, 1000, 0) 44 | pwm.start(PIN_LED_WARM) 45 | pwm.setup(PIN_LED_COLD, 1000, 0) 46 | pwm.start(PIN_LED_COLD) 47 | isPwm = 1 48 | end 49 | local power = maxPower * curPower / 10000 50 | pwm.setduty(PIN_LED_WARM, power * (1000 - colorTemp) / 1000) 51 | pwm.setduty(PIN_LED_COLD, power * colorTemp / 1000) 52 | end 53 | end 54 | 55 | 56 | function calcColorTemp (t) 57 | local tl = t + tzOffset - 3600 58 | local s = tl - (tl / 86400) * 86400 59 | s = s - (5*3600) 60 | if s < 0 or isRealTime == 0 then 61 | return 0 62 | end 63 | if s < (3*3600) then 64 | return 1000 * s / (3*3600) 65 | end 66 | s = s - (6*3600) 67 | if s < 0 then 68 | return 1000 69 | end 70 | return 1000 - 1000 * s / (13*3600) 71 | end 72 | 73 | 74 | function calcMaxPower (t) 75 | local tl = t + tzOffset 76 | local s = tl - (tl / 86400) * 86400 77 | s = s - (5*3600) 78 | if s < 0 or isRealTime == 0 then 79 | return 250 80 | end 81 | if s < (5*3600) then 82 | return 250 + 750 * s / (5*3600) 83 | end 84 | s = s - (13*3600) 85 | if s < 0 then 86 | return 1000 87 | end 88 | return 1000 - 750 * s / (6*3600) 89 | end 90 | 91 | 92 | function turnOn () 93 | print("+turnOn") 94 | state = FADEIN 95 | tmr.stop(2) 96 | tmr.alarm(2, fadeStep, tmr.ALARM_AUTO, function() 97 | curPower = curPower + 1 + 10000 * fadeStep / fadeInTime 98 | if curPower >= 10000 then 99 | curPower = 10000 100 | state = ON 101 | tmr.stop(2) 102 | end 103 | setPower() 104 | end) 105 | end 106 | 107 | 108 | function turnOff () 109 | print("+turnOff") 110 | state = FADEOUT 111 | tmr.stop(2) 112 | tmr.alarm(2, fadeStep, tmr.ALARM_AUTO, function() 113 | curPower = curPower - 1 - 10000 * fadeStep / fadeOutTime 114 | if curPower <= 0 then 115 | curPower = 0 116 | state = OFF 117 | tmr.stop(2) 118 | end 119 | setPower() 120 | end) 121 | end 122 | 123 | 124 | function getTime () 125 | local t = rtctime.get() 126 | if 0 == t then 127 | print("init time") 128 | t = 123456789 129 | rtctime.set(t, 0) 130 | isRealTime = 0 131 | end 132 | if t >= 1234567890 then 133 | isRealTime = 1 134 | end 135 | if t >= nextTimeSync then 136 | print("sync time") 137 | -- TODO: randomize time servers 138 | sntp.sync('185.22.60.71') 139 | nextTimeSync = t + (isRealTime and 7200 or 10) 140 | end 141 | return t 142 | end 143 | 144 | 145 | function updateData () 146 | temperature = ds18b20.read() or 0 147 | illuminance = adc.read(0) or 0 148 | end 149 | 150 | 151 | function sendData () 152 | --print("sendData") 153 | if PRIVATE_KEY ~= "" then 154 | local url = "/input/"..PUBLIC_KEY.."?private_key="..PRIVATE_KEY 155 | .."&colortemp="..colorTemp 156 | .."&illuminance="..illuminance 157 | .."&maxpower="..maxPower 158 | .."&lastmotion="..lastMotion 159 | .."&state="..state 160 | .."&temperature="..(temperature/10).."."..(temperature%10) 161 | --print(url) 162 | http.get("data.sparkfun.com", url, function(res) 163 | print("published:"..res) 164 | end) 165 | end 166 | end 167 | 168 | 169 | -- main loop – watch PIR senor 170 | tmr.alarm(1, watchStep, tmr.ALARM_AUTO, function() 171 | local t = getTime() 172 | local PIR = gpio.read(PIN_PIR) 173 | if gpio.HIGH == PIR then 174 | lastMotion = t 175 | end 176 | if lastMotion + motionDelay < t then 177 | if state == ON or state == FADEIN then 178 | turnOff() 179 | end 180 | else 181 | if state == OFF or state == FADEOUT then 182 | turnOn() 183 | end 184 | end 185 | maxPower = calcMaxPower(t) 186 | colorTemp = calcColorTemp(t) 187 | setPower() 188 | --print("maxPower="..maxPower.."; colorTemp="..colorTemp.."; temperature="..tostring(temperature).."; illuminance="..tostring(illuminance)) 189 | --print("PIR="..PIR.."; t="..t.."; lastMotion="..lastMotion.."; curPower="..curPower.."; state="..state) 190 | end) 191 | 192 | 193 | -- collect data and send to cloud 194 | tmr.alarm(3, sendStep, tmr.ALARM_AUTO, function() 195 | print("send data to cloud") 196 | updateData() 197 | sendData() 198 | end) 199 | 200 | 201 | -------------------------------------------------------------------------------- /nodemcu/smartclock.lua: -------------------------------------------------------------------------------- 1 | -- SmartClock for ESP8266 with nodeMCU 2 | -- Written by Sergey Martynov http://martynov.info 3 | 4 | http = require("http-client") 5 | bmp180 = require("bmp180") 6 | 7 | -- hardware settings 8 | local disp_sla = 0x3c 9 | local sda_pin = 2 -- i2c display pins 10 | local scl_pin = 1 11 | local dht_pin = 5 -- dht22 data pin 12 | 13 | -- software settings 14 | local timer_interval = 100 -- timer interval in ms 15 | local sync_interval = 90 -- sync time every n seconds 16 | local temp_interval = 3 -- update temperature/humidity/pressure every n seconds 17 | local send_interval = 60 -- send data to cloud every n seconds 18 | local debug_interval = 30 -- send debug data to cloud 19 | local draw_interval = 1 -- update display every second 20 | 21 | -- global variables 22 | local timekeep = 0 -- holds time since last sync in ms 23 | local timestamp = 0 -- holds current time in epoch seconds 24 | local timezone = 0 -- tz offset 25 | local epoch = 0 -- holds epoch of last sync 26 | local next_sync = 5 -- epoch time of next time sync 27 | local next_temp = temp_interval -- epoch time of next temp/hum/press reading 28 | local next_send = send_interval -- epoch time of next temp/hum/press sending 29 | local next_debug = debug_interval -- epoch time of next temp/hum/press sending 30 | local next_draw = draw_interval -- epoch time of next display update 31 | local tmr_prev = 0 32 | local temperature = 0 33 | local humidity = 0 34 | local pressure = 0 35 | local temperature2 = 0 36 | local disp 37 | 38 | local timediff = 0 39 | local dht_oks = 0 40 | local dht_errs = 0 41 | local dht_sum_oks = 0 42 | local dht_sum_errs = 0 43 | 44 | 45 | local function sync_time() 46 | --print("sync()") 47 | -- get current timestamp in GMT 48 | http.get("www.timeapi.org","/z/now?\\s", function(res) 49 | res = tonumber(res) 50 | if res ~= nil and res > 1444444444 then -- constant equals some past epoch 51 | timediff = timestamp - res 52 | if timediff > 1000 or timediff < -1000 then 53 | epoch = res 54 | timekeep = 0 55 | next_sync = epoch + sync_interval 56 | end 57 | --print("sync: epoch="..epoch) 58 | end 59 | end) 60 | -- get current timezone (works poorly) 61 | timezone = 3 -- XXX 62 | --[[ 63 | http.get("www.telize.com","/geoip", function(res) 64 | local data = cjson.decode(res) 65 | timezone = 0 + data.offset 66 | --print("sync: timezone="..timezone) 67 | end) 68 | --]] 69 | end 70 | 71 | 72 | local function read_temp() 73 | --print("read_temp()"..dht_pin) 74 | local status,t,h,td,hd = dht.readxx(dht_pin) 75 | --print("read: status="..status) 76 | if status == dht.OK then 77 | temperature = 10 * t + td / 100 78 | humidity = 10 * h + hd / 100 79 | --print("read_temp: temperature="..temperature.." humidity="..humidity) 80 | dht_oks = dht_oks + 1 81 | dht_errs = 0 82 | dht_sum_oks = dht_sum_oks + 1 83 | else 84 | dht_oks = 0 85 | dht_errs = dht_errs + 1 86 | dht_sum_errs = dht_sum_errs + 1 87 | end 88 | ---[[ 89 | bmp180.read(3) -- oversampling=3, i.e. 8 measurements 90 | pressure = bmp180.getPressure() 91 | temperature2 = bmp180.getTemperature() 92 | --]] 93 | end 94 | 95 | 96 | local function send_temp() 97 | --print("send_temp()") 98 | if temperature == 0 and humidity == 0 then return nil end 99 | local url = "/update?key=DQWUD59A3V9HXAYC" 100 | .. "&field1="..(temperature/10).."."..(temperature%10) 101 | .. "&field2="..(humidity/10).. "."..(humidity%10) 102 | .. "&field3="..(pressure/100).."."..(temperature%100) 103 | .. "&field4="..(temperature2/10).."."..(temperature2%10) 104 | .. "&field5="..node.heap() 105 | .. "&field6="..tmr.now() 106 | .. "&field7="..tmr.time() 107 | .. "&field8="..timekeep 108 | --print(url) 109 | http.get("api.thingspeak.com", url, function(res) 110 | --print("published:"..res) 111 | end) 112 | end 113 | 114 | local function send_debug() 115 | --print("send_debug()") 116 | local url = "/update?key=KZ6YC9VGXFCABF06" 117 | .. "&field1="..timediff 118 | .. "&field2="..dht_oks 119 | .. "&field3="..dht_errs 120 | .. "&field4="..dht_sum_oks 121 | .. "&field5="..dht_sum_errs 122 | .. "&field8="..timekeep 123 | --print(url) 124 | http.get("api.thingspeak.com", url, function(res) 125 | --print("published:"..res) 126 | end) 127 | end 128 | 129 | 130 | 131 | local function init() 132 | i2c.setup(0, sda_pin, scl_pin, i2c.SLOW) 133 | 134 | bmp180.init(sda_pin, scl_pin) 135 | 136 | disp = u8g.ssd1306_128x64_i2c(disp_sla) 137 | disp:setFont(u8g.font_6x10) 138 | disp:setFontRefHeightExtendedText() 139 | disp:setDefaultForegroundColor() 140 | disp:setFontPosTop() 141 | end 142 | 143 | 144 | local function disp_draw() 145 | -- prepare time as a string 146 | local ts = timestamp + 3600 * timezone 147 | local h = ts % 86400 / 3600 148 | local m = ts % 3600 / 60 149 | local s = ts % 60 150 | local time_str = string.format("%02u:%02u:%02u", h, m, s) 151 | -- prepare temp and humidity as strings 152 | local temp_str = (temperature/10).."."..(temperature%10)..string.char(176).."C" 153 | local hum_str = (humidity/10).."%" 154 | local press_str = (pressure/100).." hPa" 155 | -- draw on screen 156 | disp:firstPage() 157 | repeat 158 | tmr.wdclr() 159 | disp:setScale2x2() 160 | --disp:setFont(u8g.font_10x20) 161 | disp:drawStr(5, 5, time_str) 162 | disp:undoScale() 163 | --disp:setFont(u8g.font_6x10) 164 | disp:drawStr(10, 38, temp_str) 165 | disp:drawStr(80, 38, hum_str) 166 | disp:drawStr(38, 51, press_str) 167 | until disp:nextPage() == false 168 | end 169 | 170 | 171 | local function main_loop() 172 | tmr.stop(1) 173 | --tmr.wdclr() 174 | --print("KK") 175 | -- time keeping 176 | local tmr_now = tmr.now() 177 | local diff = ( tmr_now - tmr_prev ) / 1000 178 | if diff <= 0 or diff > 1000 + 2 * timer_interval then 179 | diff = timer_interval end 180 | tmr_prev = tmr_now 181 | timekeep = timekeep + diff 182 | timestamp = epoch + timekeep / 1000 183 | 184 | --print(node.heap()) 185 | --print(diff) 186 | --print("main_loop(): tmr_now="..tmr_now.." diff="..diff.." timekeep="..timekeep.." timestamp="..timestamp) 187 | --print("next_sync="..next_sync.." next_temp="..next_temp.." next_draw="..next_draw) 188 | 189 | -- time to do something else? 190 | tmr.wdclr() 191 | if timestamp >= next_sync then 192 | next_sync = timestamp + sync_interval 193 | sync_time() 194 | -- collectgarbage() 195 | elseif timestamp >= next_temp then 196 | next_temp = timestamp + temp_interval 197 | read_temp() 198 | elseif timestamp >= next_send then 199 | next_send = timestamp + send_interval 200 | send_temp() 201 | elseif timestamp >= next_debug then 202 | next_debug = timestamp + debug_interval 203 | send_debug() 204 | elseif timestamp >= next_draw then 205 | next_draw = timestamp + draw_interval 206 | disp_draw() 207 | end 208 | 209 | -- set timer for next loop 210 | -- XXX try dsleep? 211 | --tmr.wdclr() 212 | tmr.alarm(1,timer_interval,0,function() main_loop() end) 213 | end 214 | 215 | 216 | init() 217 | main_loop() 218 | 219 | --------------------------------------------------------------------------------