Merge pull request #1 from C3MA/master

Merge C3MA code back into the source
This commit is contained in:
0110 2021-01-31 12:01:09 +01:00 committed by GitHub
commit 2fc241a6af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 6650 additions and 762 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
wlancfg.lua wlancfg.lua
config.lua
*.swp *.swp
unit/testTimesMarchOctoberWithAllSeconds.lua unit/testTimesMarchOctoberWithAllSeconds.lua

View File

@ -1,6 +1,7 @@
# ESP Wordclock # ESP Wordclock
## Setup ## Setup
### Initial Setup
Install the firmware on the ESP: Install the firmware on the ESP:
The ESP must be set into the bootloader mode, like [this](https://www.ccc-mannheim.de/wiki/ESP8266#Boot_Modi) The ESP must be set into the bootloader mode, like [this](https://www.ccc-mannheim.de/wiki/ESP8266#Boot_Modi)
@ -10,11 +11,10 @@ cd os/
./flash.sh ttyUSB0 ./flash.sh ttyUSB0
</pre> </pre>
Reboot the ESP, with a serial terminal, Connect to the ESP via a terminal emulator like screen using a baud rate of 115200. Then format the filesystem and reboot the ESP with the following commands:
format the filesystem with the following command and reboot it:
<pre> <pre>
file.format() file.format()
node.reboot() node.restart()
</pre> </pre>
Then disconnect the serial terminal and copy the required files to the microcontroller: Then disconnect the serial terminal and copy the required files to the microcontroller:
@ -22,5 +22,60 @@ Then disconnect the serial terminal and copy the required files to the microcont
./tools/initialFlash.sh /dev/ttyUSB0 ./tools/initialFlash.sh /dev/ttyUSB0
</pre> </pre>
## Internal Setup ### Upgrade
Determine the IP address of your clock and execute the following script:
<pre>
./tools/remoteFlash.sh IP-Address
</pre>
## Hardware Setup
Mandatory:
* GPIO2 LEDs * GPIO2 LEDs
* GPIO0 Bootloader (at start)
* GPIO0 factory reset (long during operation)
Optinal:
* ADC VT93N2, 48k light resistor
* GPIO4 DS18B20 Temperatur sensor
## MQTT Interface
### Status
* **basetopic**/brightness **Current brightness in percent**
* **basetopic**/background **Current background color**
* **basetopic**/row1 **Current background color**
* **basetopic**/temp **Temperatur**
### Commands
* **basetopic**/cmd/single
* ON **Set brightness to 100%**
* OFF **Set brightness to 0%**
* 0-100 **Set brightness to given value**
* #rrggbb **Background color is set to hex representation of red, green and blue**
* 0-255,0-255,0-255 **Background color is set to decimal representation of red, green an blue**
* **basetopic**/cmd/telnet
* ignored **Stop MQTT server and start telnetserver at port 23**
* **basetopic**/cmd/row1
* 0-255,0-255,0-255 **Background color is set to decimal representation of red, green an blue**
* **basetopic**/cmd/row1
* 0-255,0-255,0-255 **Background color is set to decimal representation of red, green an blue**
* **basetopic**/cmd/row2
* 0-255,0-255,0-255 **Background color is set to decimal representation of red, green an blue**
* **basetopic**/cmd/row3
* 0-255,0-255,0-255 **Background color is set to decimal representation of red, green an blue**
* For all rows...
* **basetopic**/cmd/row10
* 0-255,0-255,0-255 **Background color is set to decimal representation of red, green an blue**
## OpenHAB2
Tested MQTT with binding-mqtt 2.5.x
### Configuration
```
Thing mqtt:topic:wordclock "Wordclock" (mqtt:broker) @ "MQTT" {
Channels:
Type dimmer : dim "Dimming" [ stateTopic="basetopic/brightness", commandTopic="basetopic/cmd/single" ]
Type string : cmd "Command" [ commandTopic="basetopic/cmd/single" ]
Type switch : active "Active" [ commandTopic="basetopic/cmd/single" ]
Type colorRGB : background "Background" [ stateTopic="basetopic/background", commandTopic="basetopic/cmd/single", on="28,0,0", off="0,0,0" ]
}
```

186
commands.lua Normal file
View File

@ -0,0 +1,186 @@
function storeConfig(_ssid, _password, _timezoneoffset, _sntpserver, _inv46, _dim, _fcolor, _colorMin1, _colorMin2, _colorMin3, _colorMin4, _bcolor, _threequater)
if ( (_ssid == nil) and
(_password == nil) and
(_timezoneoffset == nil) and
(_sntpserver == nil) and
(_inv46 == nil) and
(_dim == nil) and
(_fcolor == nil) and
(_colorMin1 == nil) and
(_colorMin2 == nil) and
(_colorMin3 == nil) and
(_colorMin4 == nil) and
(_bcolor == nil) and
(_threequater == nil) ) then
print("one parameter is mandatory:")
print("storeConfig(ssid, ")
print(" password,")
print(" timezoneoffset,")
print(" sntpserver,")
print(" inv46,")
print(" dim,")
print(" fcolor,")
print(" colorMin1,")
print(" colorMin2,")
print(" colorMin3,")
print(" colorMin4,")
print(" bcolor,")
print(" threequater)")
print(" ")
print("e.g.:")
print('storeConfig(nil, nil, 1, nil, "on", true, "00FF00", "00FF88", "008888", "00FF44", "004488", "000000", true)')
return
end
if (_password==nil) then
_, password, _, _ = wifi.sta.getconfig()
print("Restore password")
else
password = _password
end
if (_ssid==nil) then
ssid, _, _, _ = wifi.sta.getconfig()
else
ssid = _ssid
end
if (_sntpserver == nil) then
sntpserver = sntpserverhostname
print("Restore SNTP: " .. tostring(sntpserver))
else
sntpserver = _sntpserver
end
if (_timezoneoffset ~= nil) then
timezoneoffset = _timezoneoffset
end
if (_inv46 ~= nil) then
if ((_inv46 == true) or (_inv == "on")) then
inv46 = "on"
elseif ((_inv46 == false) or (_inv == "off")) then
inv46 = "off"
else
inv46 = "off"
end
end
if ( _dim ~= nil) then
dim = _dim
end
if (_fcolor ~= nil) then
fcolor = _fcolor
end
if (_bcolor ~= nil) then
bcolor = _bcolor
end
if (_colorMin1 ~= nil) then
colorMin1 = _colorMin1
end
if (_colorMin2 ~= nil) then
colorMin2 = _colorMin2
end
if (_colorMin3 ~= nil) then
colorMin3 = _colorMin3
end
if (_colorMin4 ~= nil) then
colorMin4 = _colorMin4
end
if (_threequater ~= nil) then
threequater = _threequater
end
print("SSID = " .. tostring(ssid))
print("TZNE = " .. tostring(timezoneoffset))
print("NTP = " .. tostring(sntpserver))
print("INVT = " .. tostring(inv46))
print("DIM = " .. tostring(dim))
print("FCOL = " .. tostring(fcolor))
print("BCOL = " .. tostring(bcolor))
print("MIN1 = " .. tostring(colorMin1))
print("MIN2 = " .. tostring(colorMin2))
print("MIN3 = " .. tostring(colorMin3))
print("MIN4 = " .. tostring(colorMin4))
print("3QRT = " .. tostring(threequater))
local configFile="config.lua"
-- Safe configuration:
file.remove(configFile .. ".new")
sec, _ = rtctime.get()
file.open(configFile.. ".new", "w+")
file.write("-- Config\n" .. "station_cfg={}\nstation_cfg.ssid=\"" .. ssid .. "\"\nstation_cfg.pwd=\"" .. password .. "\"\nstation_cfg.save=false\nwifi.sta.config(station_cfg)\n")
file.write("sntpserverhostname=\"" .. sntpserver .. "\"\n" .. "timezoneoffset=\"" .. timezoneoffset .. "\"\n".. "inv46=\"" .. tostring(inv46) .. "\"\n" .. "dim=\"" .. tostring(dim) .. "\"\n")
if (fcolor ~= nil) then
local hexColor=string.sub(fcolor, 1)
local red = tonumber(string.sub(hexColor, 1, 2), 16)
local green = tonumber(string.sub(hexColor, 3, 4), 16)
local blue = tonumber(string.sub(hexColor, 5, 6), 16)
file.write("color=string.char(" .. green .. "," .. red .. "," .. blue .. ")\n")
-- fill the current values
color=string.char(green, red, blue)
end
if (colorMin1 ~= nil) then
local hexColor=string.sub(colorMin1, 1)
local red = tonumber(string.sub(hexColor, 1, 2), 16)
local green = tonumber(string.sub(hexColor, 3, 4), 16)
local blue = tonumber(string.sub(hexColor, 5, 6), 16)
file.write("color1=string.char(" .. green .. "," .. red .. "," .. blue .. ")\n")
color1=string.char(green, red, blue)
end
if ( colorMin2 ~= nil) then
local hexColor=string.sub(colorMin2, 1)
local red = tonumber(string.sub(hexColor, 1, 2), 16)
local green = tonumber(string.sub(hexColor, 3, 4), 16)
local blue = tonumber(string.sub(hexColor, 5, 6), 16)
file.write("color2=string.char(" .. green .. "," .. red .. "," .. blue .. ")\n")
color2=string.char(green, red, blue)
end
if ( colorMin3 ~= nil) then
local hexColor=string.sub(colorMin3, 1)
local red = tonumber(string.sub(hexColor, 1, 2), 16)
local green = tonumber(string.sub(hexColor, 3, 4), 16)
local blue = tonumber(string.sub(hexColor, 5, 6), 16)
file.write("color3=string.char(" .. green .. "," .. red .. "," .. blue .. ")\n")
color3=string.char(green, red, blue)
end
if ( colorMin4 ~= nil) then
local hexColor=string.sub(colorMin4, 1)
local red = tonumber(string.sub(hexColor, 1, 2), 16)
local green = tonumber(string.sub(hexColor, 3, 4), 16)
local blue = tonumber(string.sub(hexColor, 5, 6), 16)
file.write("color4=string.char(" .. green .. "," .. red .. "," .. blue .. ")\n")
color4=string.char(green, red, blue)
end
if ( bcolor ~= nil) then
local hexColor=string.sub(bcolor, 1)
local red = tonumber(string.sub(hexColor, 1, 2), 16)
local green = tonumber(string.sub(hexColor, 3, 4), 16)
local blue = tonumber(string.sub(hexColor, 5, 6), 16)
file.write("colorBg=string.char(" .. green .. "," .. red .. "," .. blue .. ")\n")
-- fill the current values
colorBg=string.char(green, red, blue)
end
if (getTime ~= nil) then
time = getTime(sec, timezoneoffset)
file.write("print(\"Config from " .. time.year .. "-" .. time.month .. "-" .. time.day .. " " .. time.hour .. ":" .. time.minute .. ":" .. time.second .. "\")\n")
end
if ( threequater ~= nil) then
file.write("threequater=true\n")
-- fill the current values
threequater=true
else
file.write("threequater=nil\n") -- unset threequater
-- fill the current values
threequater=nil
end
file.close()
collectgarbage()
sec=nil
file.remove(configFile)
if (file.rename(configFile .. ".new", configFile)) then
print("Rename Successfully")
else
print("Cannot rename " .. configFile .. ".new")
end
end

View File

@ -1,26 +1,18 @@
-- Module filling a buffer, sent to the LEDs -- Module filling a buffer, sent to the LEDs
local M
function updateColor(data) do
if (data.usedCharacters <= data.charsPerMinute) then local updateColor = function (data)
if (data.words.min1 == 1 or data.words.min2 == 1 or data.words.min3 == 1 or data.words.min4 == 1) then if (data.amountOfChars > 0) then
local div = tonumber(data.drawnCharacters/data.amountOfChars)
if (div < 1) then
return data.colorFg
elseif (div < 2) then
return data.colorMin1 return data.colorMin1
else elseif (div < 3) then
return data.colorFg
end
elseif (data.usedCharacters <= data.charsPerMinute*2) then
if (data.words.min2 == 1 or data.words.min3 == 1 or data.words.min4 == 1) then
return data.colorMin2 return data.colorMin2
else elseif (div < 4) then
return data.colorFg
end
elseif (data.usedCharacters <= data.charsPerMinute*3) then
if (data.words.min3 == 1 or data.words.min4 == 1) then
return data.colorMin3 return data.colorMin3
else elseif (div < 5) then
return data.colorFg
end
elseif (data.usedCharacters > data.charsPerMinute*3) then
if (data.words.min4 == 1) then
return data.colorMin4 return data.colorMin4
else else
return data.colorFg return data.colorFg
@ -30,7 +22,10 @@ function updateColor(data)
end end
end end
function drawLEDs(data, numberNewChars) local drawLEDs = function(data, numberNewChars)
if (numberNewChars == nil) then
numberNewChars=0
end
local tmpBuf=nil local tmpBuf=nil
for i=1,numberNewChars do for i=1,numberNewChars do
if (tmpBuf == nil) then if (tmpBuf == nil) then
@ -38,165 +33,311 @@ function drawLEDs(data, numberNewChars)
else else
tmpBuf=tmpBuf .. updateColor(data) tmpBuf=tmpBuf .. updateColor(data)
end end
data.usedCharacters=data.usedCharacters+1 data.drawnCharacters=data.drawnCharacters+1
end end
return tmpBuf return tmpBuf
end end
-- Utility function for round
local round = function(num)
under = math.floor(num)
upper = math.floor(num) + 1
underV = -(under - num)
upperV = upper - num
if (upperV > underV) then
return under
else
return upper
end
end
local data={}
-- Module displaying of the words -- Module displaying of the words
function generateLEDs(words, colorFg, colorMin1, colorMin2, colorMin3, colorMin4, characters) local generateLEDs = function(words, colorBg, colorFg, colorMin1, colorMin2, colorMin3, colorMin4, invertRows, amountOfChars)
-- Set the local variables needed for the colored progress bar -- Set the local variables needed for the colored progress bar
data={} if (words == nil) then
data.charsPerMinute=math.floor(characters/3) -- devide by three (Minute 1 to Minute 3, Minute 4 takes the last chars) return nil
data.words=words end
if (invertRows == nil) then
invertRows=false
end
local minutes=1
if (words.min1 == 1) then
minutes = minutes + 1
elseif (words.min2 == 1) then
minutes = minutes + 2
elseif (words.min3 == 1) then
minutes = minutes + 3
elseif (words.min4 == 1) then
minutes = minutes + 4
end
-- always set a foreground value
if (colorFg == nil) then
colorFg = string.char(255,255,255)
end
if (amountOfChars ~= nil) then
data.amountOfChars = amountOfChars/minutes
else
data.amountOfChars = 0
end
if ( (adc ~= nil) and (words.briPercent ~= nil) ) then
local per = math.floor(100*adc.read(0)/1000)
words.briPercent = tonumber( ((words.briPercent * 4) + per) / 5)
print("Minutes : " .. tostring(minutes) .. " bright: " .. tostring(words.briPercent) .. "% current: " .. tostring(per) .. "%")
data.colorFg = string.char(string.byte(colorFg,1) * briPercent / 100, string.byte(colorFg,2) * briPercent / 100, string.byte(colorFg,3) * briPercent / 100)
data.colorMin1 = string.char(string.byte(colorMin1,1) * briPercent / 100, string.byte(colorMin1,2) * briPercent / 100, string.byte(colorMin1,3) * briPercent / 100)
data.colorMin2 = string.char(string.byte(colorMin2,1) * briPercent / 100, string.byte(colorMin2,2) * briPercent / 100, string.byte(colorMin2,3) * briPercent / 100)
data.colorMin3 = string.char(string.byte(colorMin3,1) * briPercent / 100, string.byte(colorMin3,2) * briPercent / 100, string.byte(colorMin3,3) * briPercent / 100)
data.colorMin4 = string.char(string.byte(colorMin4,1) * briPercent / 100, string.byte(colorMin4,2) * briPercent / 100, string.byte(colorMin4,3) * briPercent / 100)
else
-- devide by five (Minute 0, Minute 1 to Minute 4 takes the last chars)
data.colorFg=colorFg data.colorFg=colorFg
data.colorMin1=colorMin1 data.colorMin1=colorMin1
data.colorMin2=colorMin2 data.colorMin2=colorMin2
data.colorMin3=colorMin3 data.colorMin3=colorMin3
data.colorMin4=colorMin4 data.colorMin4=colorMin4
data.usedCharacters=0 end
data.drawnCharacters=0
local charsPerLine=11
-- Space / background has no color by default
local space=string.char(0,0,0) local space=string.char(0,0,0)
-- update the background color, if set
-- Background color must always be set
if (colorBg ~= nil) then if (colorBg ~= nil) then
space = colorBg space = colorBg
else
colorBg = space
end end
-- Set the foreground color as the default color -- Set the foreground color as the default color
local buf=colorFg local buf=data.colorFg
local line=space
-- line 1---------------------------------------------- -- line 1----------------------------------------------
if (words.itis == 1) then if (rowbgColor[1] ~= nil) then
buf=drawLEDs(data,2) -- ES space = rowbgColor[1]
print(tostring(buf))
-- K fill character
buf=buf .. space:rep(1)
buf=buf .. drawLEDs(data,3) -- IST
-- L fill character
buf=buf .. space:rep(1)
else
buf=space:rep(7)
end end
if (words.fiveMin== 1) then if (words.it==1) then
buf=drawLEDs(data,2) -- ES
else
buf=space:rep(2)
end
-- K fill character
buf=buf .. space:rep(1)
if (words.is == 1) then
buf=buf .. drawLEDs(data,3) -- IST
else
buf=buf .. space:rep(3)
end
-- L fill character
buf=buf .. space:rep(1)
if (words.fiveMin== 1) then
buf= buf .. drawLEDs(data,4) -- FUENF buf= buf .. drawLEDs(data,4) -- FUENF
else else
buf= buf .. space:rep(4) buf= buf .. space:rep(4)
end end
-- line 2-- even row (so inverted) -------------------- -- line 2-- even row (so inverted) --------------------
if (words.twenty == 1) then if (rowbgColor[2] ~= nil) then
buf= buf .. drawLEDs(data,7) -- ZWANZIG space = rowbgColor[2]
else else
buf= buf .. space:rep(7) space = colorBg
end end
if (words.tenMin == 1) then if (words.tenMin == 1) then
buf= buf .. drawLEDs(data,4) -- ZEHN line= drawLEDs(data,4) -- ZEHN
else else
buf= buf .. space:rep(4) line= space:rep(4)
end end
-- line3---------------------------------------------- if (words.twenty == 1) then
if (words.threequater == 1) then line= line .. drawLEDs(data,7) -- ZWANZIG
buf= buf .. drawLEDs(data,11) -- Dreiviertel
elseif (words.quater == 1) then
buf= buf .. space:rep(4)
buf= buf .. drawLEDs(data,7) -- VIERTEL
else else
buf= buf .. space:rep(11) line= line .. space:rep(7)
end end
--line 4-------- even row (so inverted) ------------- -- fill, the buffer
if (words.before == 1) then for i = 0,10 do
buf=buf .. space:rep(2) buf = buf .. line:sub((11-i)*3-2,(11-i)*3)
buf= buf .. drawLEDs(data,3) -- VOR
else
buf= buf .. space:rep(5)
end
if (words.after == 1) then
buf= buf .. drawLEDs(data,4) -- NACH
buf= buf .. space:rep(2) -- TG
else
buf= buf .. space:rep(6)
end
------------------------------------------------
if (words.half == 1) then
buf= buf .. drawLEDs(data,4) -- HALB
buf= buf .. space:rep(1) -- X
else
buf= buf .. space:rep(5)
end
if (words.twelve == 1) then
buf= buf .. drawLEDs(data,5) -- ZWOELF
buf= buf .. space:rep(1) -- P
else
buf= buf .. space:rep(6)
end
------------even row (so inverted) ---------------------
if (words.seven == 1) then
buf= buf .. drawLEDs(data,6) -- SIEBEN
buf= buf .. space:rep(5)
elseif (words.oneLong == 1) then
buf= buf .. space:rep(5)
buf= buf .. drawLEDs(data,4) -- EINS
buf= buf .. space:rep(2)
elseif (words.one == 1) then
buf= buf .. space:rep(6)
buf= buf .. drawLEDs(data,3) -- EIN
buf= buf .. space:rep(2)
elseif (words.two == 1) then
buf= buf .. space:rep(7)
buf= buf .. drawLEDs(data,4) -- ZWEI
else
buf= buf .. space:rep(11)
end
------------------------------------------------
if (words.three == 1) then
buf= buf .. space:rep(1)
buf= buf .. drawLEDs(data,4) -- DREI
buf= buf .. space:rep(6)
elseif (words.five == 1) then
buf= buf .. space:rep(7)
buf= buf .. drawLEDs(data,4) -- FUENF
else
buf= buf .. space:rep(11)
end
------------even row (so inverted) ---------------------
if (words.four == 1) then
buf= buf .. drawLEDs(data,4) -- VIER
buf= buf .. space:rep(7)
elseif (words.nine == 1) then
buf= buf .. space:rep(4)
buf= buf .. drawLEDs(data,4) -- NEUN
buf= buf .. space:rep(3)
elseif (words.eleven == 1) then
buf= buf .. space:rep(8)
buf= buf .. drawLEDs(data,3) -- ELEVEN
else
buf= buf .. space:rep(11)
end
------------------------------------------------
if (words.eight == 1) then
buf= buf .. space:rep(1)
buf= buf .. drawLEDs(data,4) -- ACHT
buf= buf .. space:rep(6)
elseif (words.ten == 1) then
buf= buf .. space:rep(5)
buf= buf .. drawLEDs(data,4) -- ZEHN
buf= buf .. space:rep(2)
else
buf= buf .. space:rep(11)
end
------------even row (so inverted) ---------------------
if (words.clock == 1) then
buf= buf .. drawLEDs(data,3) -- UHR
else
buf= buf .. space:rep(3)
end
if (words.six == 1) then
buf= buf .. space:rep(2)
buf= buf .. drawLEDs(data,5) -- SECHS
buf= buf .. space:rep(1)
else
buf= buf .. space:rep(8)
end end
-- line3----------------------------------------------
if (rowbgColor[3] ~= nil) then
space = rowbgColor[3]
else
space = colorBg
end
if (words.threequater == 1) then
line= drawLEDs(data,11) -- Dreiviertel
elseif (words.quater == 1) then
line= space:rep(4)
line= line .. drawLEDs(data,7) -- VIERTEL
else
line= space:rep(11)
end
-- fill, the buffer
buf = buf .. line
--line 4-------- even row (so inverted) -------------
if (rowbgColor[4] ~= nil) then
space = rowbgColor[4]
else
space = colorBg
end
if (words.after == 1) then
line= space:rep(2) -- TG
line= line .. drawLEDs(data,4) -- NACH
else
line= space:rep(6)
end
if (words.before == 1) then
line= line .. drawLEDs(data,3) -- VOR
line= line .. space:rep(2)
else
line= line .. space:rep(5)
end
if (invertRows == true) then
buf = buf .. line
else
for i = 0,10 do
buf = buf .. line:sub((11-i)*3-2,(11-i)*3)
end
end
------------------------------------------------
if (rowbgColor[5] ~= nil) then
space = rowbgColor[5]
else
space = colorBg
end
if (words.half == 1) then
line= drawLEDs(data,4) -- HALB
line= line .. space:rep(1) -- X
else
line= space:rep(5)
end
if (words.twelve == 1) then
line= line .. drawLEDs(data,5) -- ZWOELF
line= line .. space:rep(1) -- P
else
line= line .. space:rep(6)
end
if (invertRows == true) then
for i = 0,10 do
buf = buf .. line:sub((11-i)*3-2,(11-i)*3)
end
else
buf=buf .. line
end
------------even row (so inverted) ---------------------
if (rowbgColor[6] ~= nil) then
space = rowbgColor[6]
else
space = colorBg
end
if (words.seven == 1) then
line= space:rep(5)
line= line .. drawLEDs(data,6) -- SIEBEN
elseif (words.oneLong == 1) then
line= space:rep(2)
line= line .. drawLEDs(data,4) -- EINS
line= line .. space:rep(5)
elseif (words.one == 1) then
line= space:rep(2)
line= line .. drawLEDs(data,3) -- EIN
line= line .. space:rep(6)
elseif (words.two == 1) then
line= drawLEDs(data,4) -- ZWEI
line= line .. space:rep(7)
else
line= space:rep(11)
end
if (invertRows == true) then
buf = buf .. line
else
for i = 0,10 do
buf = buf .. line:sub((11-i)*3-2,(11-i)*3)
end
end
------------------------------------------------
if (rowbgColor[7] ~= nil) then
space = rowbgColor[7]
else
space = colorBg
end
if (words.three == 1) then
line= space:rep(1)
line= line .. drawLEDs(data,4) -- DREI
line= line .. space:rep(6)
elseif (words.five == 1) then
line= space:rep(7)
line= line .. drawLEDs(data,4) -- FUENF
else
line= space:rep(11)
end
buf = buf .. line
------------even row (so inverted) ---------------------
if (rowbgColor[8] ~= nil) then
space = rowbgColor[8]
else
space = colorBg
end
if (words.four == 1) then
line= space:rep(7)
line= line .. drawLEDs(data,4) -- VIER
elseif (words.nine == 1) then
line= space:rep(3)
line= line .. drawLEDs(data,4) -- NEUN
line= line .. space:rep(4)
elseif (words.eleven == 1) then
line= drawLEDs(data,3) -- ELEVEN
line= line .. space:rep(8)
else
line= space:rep(11)
end
for i = 0,10 do
buf = buf .. line:sub((11-i)*3-2,(11-i)*3)
end
------------------------------------------------
if (rowbgColor[9] ~= nil) then
space = rowbgColor[9]
else
space = colorBg
end
if (words.eight == 1) then
line= space:rep(1)
line= line .. drawLEDs(data,4) -- ACHT
line= line .. space:rep(6)
elseif (words.ten == 1) then
line= space:rep(5)
line= line .. drawLEDs(data,4) -- ZEHN
line= line .. space:rep(2)
else
line= space:rep(11)
end
buf = buf .. line
------------even row (so inverted) ---------------------
if (rowbgColor[10] ~= nil) then
space = rowbgColor[10]
else
space = colorBg
end
if (words.six == 1) then
line= space:rep(1)
line= line .. drawLEDs(data,5) -- SECHS
line= line .. space:rep(2)
else
line= space:rep(8)
end
if (words.clock == 1) then
line= line .. drawLEDs(data,3) -- UHR
else
line= line .. space:rep(3)
end
for i = 0,10 do
buf = buf .. line:sub((11-i)*3-2,(11-i)*3)
end
------ Minutes -----------
if (words.min1 == 1) then if (words.min1 == 1) then
buf= buf .. colorFg buf= buf .. colorFg
else else
@ -220,3 +361,77 @@ function generateLEDs(words, colorFg, colorMin1, colorMin2, colorMin3, colorMin4
collectgarbage() collectgarbage()
return buf return buf
end end
-- Count amount of characters to display
local countChars = function(words)
local characters = 0
for key,value in pairs(words) do
if (value > 0) then
if (key == "it") then
characters = characters + 2
elseif (key == "is") then
characters = characters + 3
elseif (key == "fiveMin") then
characters = characters + 4
elseif (key == "tenMin") then
characters = characters + 4
elseif (key == "after") then
characters = characters + 4
elseif (key == "before") then
characters = characters + 3
elseif (key == "threeHour") then
characters = characters + 4
elseif (key == "quater") then
characters = characters + 7
elseif (key == "threequater") then
characters = characters + 11
elseif (key == "half") then
characters = characters + 4
elseif (key == "one") then
characters = characters + 3
elseif (key == "oneLong") then
characters = characters + 4
elseif (key == "two") then
characters = characters + 4
elseif (key == "three") then
characters = characters + 4
elseif (key == "four") then
characters = characters + 4
elseif (key == "five") then
characters = characters + 4
elseif (key == "six") then
characters = characters + 4
elseif (key == "seven") then
characters = characters + 6
elseif (key == "eight") then
characters = characters + 4
elseif (key == "nine") then
characters = characters + 4
elseif (key == "ten") then
characters = characters + 4
elseif (key == "eleven") then
characters = characters + 3
elseif (key == "twelve") then
characters = characters + 5
elseif (key == "twenty") then
characters = characters + 7
elseif (key == "clock") then
characters = characters + 3
elseif (key == "sr_nc") then
characters = characters + 3
end
end
end
return characters
end
M = {
generateLEDs = generateLEDs,
round = round,
drawLEDs = drawLEDs,
updateColor = updateColor,
data = data,
countChars = countChars
}
end
displayword = M

138
ds18b20.lua Normal file
View File

@ -0,0 +1,138 @@
--------------------------------------------------------------------------------
-- DS18B20 one wire module for NODEMCU
-- NODEMCU TEAM
-- LICENCE: http://opensource.org/licenses/MIT
-- Vowstar <vowstar@nodemcu.com>
-- 2015/02/14 sza2 <sza2trash@gmail.com> Fix for negative values
--------------------------------------------------------------------------------
-- Set module name as parameter of require
local modname = ...
local M = {}
_G[modname] = M
--------------------------------------------------------------------------------
-- Local used variables
--------------------------------------------------------------------------------
-- DS18B20 dq pin
local pin = nil
-- DS18B20 default pin
local defaultPin = 9
--------------------------------------------------------------------------------
-- Local used modules
--------------------------------------------------------------------------------
-- Table module
local table = table
-- String module
local string = string
-- One wire module
local ow = ow
-- Timer module
local tmr = tmr
-- Limited to local environment
setfenv(1,M)
--------------------------------------------------------------------------------
-- Implementation
--------------------------------------------------------------------------------
C = 0
F = 1
K = 2
function setup(dq)
pin = dq
if(pin == nil) then
pin = defaultPin
end
ow.setup(pin)
end
function addrs()
setup(pin)
tbl = {}
ow.reset_search(pin)
repeat
addr = ow.search(pin)
if(addr ~= nil) then
table.insert(tbl, addr)
end
tmr.wdclr()
until (addr == nil)
ow.reset_search(pin)
return tbl
end
function readNumber(addr, unit)
result = nil
setup(pin)
flag = false
if(addr == nil) then
ow.reset_search(pin)
count = 0
repeat
count = count + 1
addr = ow.search(pin)
tmr.wdclr()
until((addr ~= nil) or (count > 100))
ow.reset_search(pin)
end
if(addr == nil) then
return result
end
crc = ow.crc8(string.sub(addr,1,7))
if (crc == addr:byte(8)) then
if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then
-- print("Device is a DS18S20 family device.")
ow.reset(pin)
ow.select(pin, addr)
ow.write(pin, 0x44, 1)
-- tmr.delay(1000000)
present = ow.reset(pin)
ow.select(pin, addr)
ow.write(pin,0xBE,1)
-- print("P="..present)
data = nil
data = string.char(ow.read(pin))
for i = 1, 8 do
data = data .. string.char(ow.read(pin))
end
-- print(data:byte(1,9))
crc = ow.crc8(string.sub(data,1,8))
-- print("CRC="..crc)
if (crc == data:byte(9)) then
t = (data:byte(1) + data:byte(2) * 256)
if (t > 32767) then
t = t - 65536
end
if(unit == nil or unit == C) then
t = t * 625
elseif(unit == F) then
t = t * 1125 + 320000
elseif(unit == K) then
t = t * 625 + 2731500
else
return nil
end
t = t / 100
-- print("Temperature="..t1.."."..t2.." Centigrade")
-- result = t1.."."..t2
return t
end
tmr.wdclr()
else
-- print("Device family is not recognized.")
end
else
-- print("CRC is not valid!")
end
return result
end
function read(addr, unit)
t = readNumber(addr, unit)
if (t == nil) then
return nil
else
return t
end
end
-- Return module table
return M

View File

@ -1,22 +0,0 @@
<html>
<head><title>WordClock Setup Page</title>
</head><body>
<h1>Welcome to the WordClock</h1>
<form action="" method="POST">
<table>
<tr><th>WIFI-SSID</b></th><td><input name="ssid" value="${SSID}"></td><td /></tr>
<tr><th>WIFI-Password</th><td><input name="password"></td><td /></tr>
<tr><th>SNTP Server</th><td><input name="sntpserver" value="${sntpserverhostname}" ></td><td>ntp server to sync the time</tr>
<tr><th>Offset to UTC time</th><td><input type="number" name="timezoneoffset" value="${TIMEZONEOFFSET}"></td><td>Define the offset to UTC time in hours. E.g +1</tr>
<tr><th>Color</th><td><input type="color" name="fcolor" value="${HEXCOLOR}"></td><td /></tr>
<tr><th>1. Minute Color</th><td><input type=\"color\" name=\"colorMin1\" value=\"" .. hexColor1 .. "\"></td><td /></tr>"
<tr><th>2. Minute Color</th><td><input type=\"color\" name=\"colorMin2\" value=\"" .. hexColor2 .. "\"></td><td /></tr>"
<tr><th>3. Minute Color</th><td><input type=\"color\" name=\"colorMin3\" value=\"" .. hexColor3 .. "\"></td><td /></tr>"
<tr><th>4. Minute Color</th><td><input type=\"color\" name=\"colorMin4\" value=\"" .. hexColor4 .. "\"></td><td /></tr>"
<tr><th>Three quater</th><td><input type="checkbox" name="threequater" ${THREEQUARTERCHECKED}></td><td>Dreiviertel Joa/nei</td></tr>
<tr><th>ColorMode</th><td><input type="checkbox" name="colorMode" ${COLORMODECHECKED}></td><td>If checked, words are dark, rest is colored</td></tr>
<tr><td colspan="3"><div align="center"><input type="submit" value="Save Configuration" onclick="this.value='Submitting ..';this.disabled='disabled'; this.form.submit();"></div></td></tr>
<tr><td colspan="3"><div align="center"><input type="submit" name="action" value="Reboot"></div></td></tr>
</table></form>
$ADDITIONAL_LINE
</body></html>

View File

@ -7,11 +7,13 @@ counter1=0
ws2812.write(string.char(0,0,0):rep(114)) ws2812.write(string.char(0,0,0):rep(114))
tmr.alarm(2, 85, 1, function() tmr.alarm(2, 85, 1, function()
counter1=counter1+1 counter1=counter1+1
ws2812.write(string.char(128,0,0):rep(counter1) .. string.char(0,0,0):rep(MAXLEDS - (counter1*2)) .. string.char(0,0,64):rep(counter1)) spaceLeds = math.max(MAXLEDS - (counter1*2), 0)
ws2812.write(string.char(128,0,0):rep(counter1) .. string.char(0,0,0):rep(spaceLeds) .. string.char(0,0,64):rep(counter1))
end) end)
local blacklistfile="init.lua config.lua config.lua.new" local blacklistfile="init.lua config.lua config.lua.new webpage.html"
function recompileAll() function recompileAll()
for i=0,5 do tmr.stop(i) end
-- compile all files -- compile all files
l = file.list(); l = file.list();
for k,_ in pairs(l) do for k,_ in pairs(l) do
@ -34,15 +36,28 @@ end
function mydofile(mod) function mydofile(mod)
if (file.open(mod .. ".lua")) then if (file.open(mod .. ".lua")) then
dofile( mod .. ".lua") dofile( mod .. ".lua")
else elseif (file.open(mod .. ".lc")) then
dofile(mod .. ".lc") dofile(mod .. ".lc")
else
print("Error: " .. mod)
end end
end end
tmr.alarm(1, 5000, 0, function() tmr.alarm(1, 5000, 0, function()
tmr.stop(2) tmr.stop(2)
if (file.open("main.lua")) then if (
(file.open("main.lua")) or
(file.open("timecore.lua")) or
(file.open("wordclock.lua")) or
(file.open("displayword.lua")) or
(file.open("mqtt.lua")) or
(file.open("ds18b20.lua")) or
(file.open("telnet.lua"))
) then
c = string.char(0,128,0)
w = string.char(0,0,0)
ws2812.write(w:rep(4) .. c .. w:rep(15) .. c .. w:rep(9) .. c .. w:rep(30) .. c .. w:rep(41) .. c )
recompileAll() recompileAll()
print("Rebooting ...") print("Rebooting ...")
-- reboot repairs everything -- reboot repairs everything
@ -54,3 +69,4 @@ tmr.alarm(1, 5000, 0, function()
print("No Main file found") print("No Main file found")
end end
end) end)
print("Init file end reached")

148
main.lua
View File

@ -1,5 +1,7 @@
-- Main Module -- Main Module
displayword = {}
function startSetupMode() function startSetupMode()
tmr.stop(0) tmr.stop(0)
tmr.stop(1) tmr.stop(1)
@ -29,36 +31,62 @@ end
function syncTimeFromInternet() function syncTimeFromInternet()
--ptbtime1.ptb.de if (syncRunning == nil) then
syncRunning=true
sntp.sync(sntpserverhostname, sntp.sync(sntpserverhostname,
function(sec,usec,server) function(sec,usec,server)
print('sync', sec, usec, server) print('sync', sec, usec, server)
displayTime() displayTime()
syncRunning=nil
end, end,
function() function()
print('failed!') print('failed!')
syncRunning=nil
end end
) )
end
end end
briPercent = 50
function displayTime() function displayTime()
sec, usec = rtctime.get() local sec, usec = rtctime.get()
-- Handle lazy programmer: -- Handle lazy programmer:
if (timezoneoffset == nil) then if (timezoneoffset == nil) then
timezoneoffset=0 timezoneoffset=0
end end
time = getTime(sec, timezoneoffset) local time = getTime(sec, timezoneoffset)
words = display_timestat(time.hour, time.minute) local words = display_timestat(time.hour, time.minute)
if ((dim ~= nil) and (dim == "on")) then
local charactersOfTime = display_countwords_de(words) words.briPercent=briPercent
ledBuf = generateLEDs(words, color, color1, color2, color3, color4, if (words.briPercent ~= nil and words.briPercent < 3) then
charactersOfTime) words.briPercent=3
end
print("Local time : " .. time.year .. "-" .. time.month .. "-" .. time.day .. " " .. time.hour .. ":" .. time.minute .. ":" .. time.second .. " in " .. charactersOfTime .. " chars") else
words.briPercent=nil
-- Write the buffer to the LEDs end
dofile("displayword.lc")
if (displayword ~= nil) then
--if lines 4 to 6 are inverted due to hardware-fuckup, unfuck it here
local invertRows=false
if ((inv46 ~= nil) and (inv46 == "on")) then
invertRows=true
end
local characters = displayword.countChars(words)
ledBuf = displayword.generateLEDs(words, colorBg, color, color1, color2, color3, color4, invertRows, characters)
end
displayword = nil
if (ledBuf ~= nil) then
ws2812.write(ledBuf) ws2812.write(ledBuf)
else
if ((colorBg ~= nil) and (color ~= nil)) then
ws2812.write(colorBg:rep(107) .. color:rep(3))
else
local space=string.char(0,0,0)
-- set FG to fix value:
colorFg = string.char(255,0,0)
ws2812.write(space:rep(107) .. colorFg:rep(3))
end
end
-- Used for debugging -- Used for debugging
if (clockdebug ~= nil) then if (clockdebug ~= nil) then
for key,value in pairs(words) do for key,value in pairs(words) do
@ -68,7 +96,7 @@ function displayTime()
end end
end end
-- cleanup -- cleanup
ledBuf=nil briPercent=words.briPercent
words=nil words=nil
time=nil time=nil
collectgarbage() collectgarbage()
@ -80,18 +108,41 @@ function normalOperation()
-- Color is defined as GREEN, RED, BLUE -- Color is defined as GREEN, RED, BLUE
color=string.char(0,0,250) color=string.char(0,0,250)
end end
print("Fg Color: " .. tostring(string.byte(color,1)) .. "x" .. tostring(string.byte(color,2)) .. "x" .. tostring(string.byte(color,3)) )
connect_counter=0 connect_counter=0
-- Wait to be connect to the WiFi access point. -- Wait to be connect to the WiFi access point.
tmr.alarm(0, 1000, 1, function() tmr.alarm(0, 500, 1, function()
connect_counter=connect_counter+1 connect_counter=connect_counter+1
if wifi.sta.status() ~= 5 then if wifi.sta.status() ~= 5 then
print(connect_counter .. "/60 Connecting to AP...") print(connect_counter .. "/60 Connecting to AP...")
if (connect_counter % 2 == 0) then if (connect_counter % 5 ~= 4) then
local wlanColor=string.char((connect_counter % 6)*20,(connect_counter % 5)*20,(connect_counter % 3)*20) local wlanColor=string.char((connect_counter % 6)*20,(connect_counter % 5)*20,(connect_counter % 3)*20)
local space=string.char(0,0,0) local space=string.char(0,0,0)
local buf=space:rep(6) .. wlanColor .. space:rep(4) local buf=space:rep(6)
buf= buf .. space:rep(3) .. wlanColor:rep(3) if ((connect_counter % 5) >= 1) then
buf = buf .. wlanColor
else
buf = buf .. space
end
buf = buf .. space:rep(4)
buf= buf .. space:rep(3)
if ((connect_counter % 5) >= 3) then
buf = buf .. wlanColor
else
buf = buf .. space
end
if ((connect_counter % 5) >= 2) then
buf = buf .. wlanColor
else
buf = buf .. space
end
if ((connect_counter % 5) >= 0) then
buf = buf .. wlanColor
else
buf = buf .. space
end
buf = buf .. space:rep(100)
ws2812.write(buf) ws2812.write(buf)
else else
ws2812.write(string.char(0,0,0):rep(114)) ws2812.write(string.char(0,0,0):rep(114))
@ -101,24 +152,41 @@ function normalOperation()
print('IP: ',wifi.sta.getip()) print('IP: ',wifi.sta.getip())
-- Here the WLAN is found, and something is done -- Here the WLAN is found, and something is done
print("Solving dependencies") print("Solving dependencies")
local dependModules = { "timecore" , "wordclock", "displayword" } local dependModules = { "timecore" , "wordclock", "mqtt" }
for _,mod in pairs(dependModules) do for _,mod in pairs(dependModules) do
print("Loading " .. mod) print("Loading " .. mod)
mydofile(mod) mydofile(mod)
end end
tmr.alarm(2, 500, 0 ,function() setupCounter=5
tmr.alarm(1, 5000, 1 ,function()
if (setupCounter > 4) then
syncTimeFromInternet() syncTimeFromInternet()
end) setupCounter=setupCounter-1
tmr.alarm(3, 2000, 0 ,function() elseif (setupCounter > 3) then
print("Start webserver...") if (startMqttClient ~= nil) then
mydofile("webserver") startMqttClient()
startWebServer() else
print("NO Mqtt found")
mydofile("telnet")
end
setupCounter=setupCounter-1
elseif (setupCounter > 2) then
if (startTelnetServer ~= nil) then
startTelnetServer()
else
displayTime()
end
setupCounter=setupCounter-1
else
displayTime()
end
collectgarbage()
end) end)
displayTime() -- sync the time every 5 minutes
-- Start the time Thread tmr.alarm(2, 300003, 1 ,function()
tmr.alarm(1, 20000, 1 ,function() syncTimeFromInternet()
displayTime() displayTime()
end) end)
@ -132,15 +200,37 @@ function normalOperation()
end end
function stopWordclock()
for i=0,5,1 do tmr.stop(i) end
end
-------------------main program ----------------------------- -------------------main program -----------------------------
ws2812.init() -- WS2812 LEDs initialized on GPIO2 ws2812.init() -- WS2812 LEDs initialized on GPIO2
if ( file.open("config.lua") ) then if ( file.open("config.lua") ) then
--- Normal operation --- Normal operation
wifi.setmode(wifi.STATION) wifi.setmode(wifi.STATION)
dofile("config.lua") mydofile("config")
normalOperation() normalOperation()
else else
-- Logic for inital setup -- Logic for inital setup
startSetupMode() startSetupMode()
end end
----------- button ---------
gpio.mode(3, gpio.INPUT)
btnCounter=0
-- Start the time Thread
tmr.alarm(4, 500, 1 ,function()
if (gpio.read(3) == 0) then
tmr.stop(1) -- stop the LED thread
print("Button pressed " .. tostring(btnCounter))
btnCounter = btnCounter + 5
local ledBuf= string.char(128,0,0):rep(btnCounter) .. string.char(0,0,0):rep(110 - btnCounter)
ws2812.write(ledBuf)
if (btnCounter >= 110) then
file.remove("config.lua")
file.remove("config.lc")
node.restart()
end
end
end)

169
mqtt.lua Normal file
View File

@ -0,0 +1,169 @@
-- Global variable
m=nil
mqttConnected = false
-- Temp:
t=nil
rowbgColor= {}
function handleSingleCommand(client, topic, data)
if (data == "ON") then
briPercent=100
m:publish(mqttPrefix .. "/clock", "ON", 0, 0)
elseif (data == "OFF") then
briPercent=0
m:publish(mqttPrefix .. "/clock", "OFF", 0, 0)
elseif ((data:sub(1,1) == "#" and data:len() == 7) or (string.match(data, "%d+,%d+,%d+"))) then
local red=0
local green=0
local blue=0
if (data:sub(1,1) == "#") then
red = tonumber(data:sub(2,3), 16)
green = tonumber(data:sub(4,5), 16)
blue = tonumber(data:sub(6,7), 16)
else
red, green, blue = string.match(data, "(%d+),(%d+),(%d+)")
end
colorBg=string.char(green * briPercent / 100, red * briPercent / 100, blue * briPercent / 100)
print("Updated BG: " .. tostring(red) .. "," .. tostring(green) .. "," .. tostring(blue) )
m:publish(mqttPrefix .. "/background", tostring(red) .. "," .. tostring(green) .. "," .. tostring(blue), 0, 0)
if (displayTime~= nil) then
displayTime()
end
else
if (tonumber(data) >= 0 and tonumber(data) <= 100) then
briPercent=tonumber(data)
m:publish(mqttPrefix .. "/clock", tostring(data), 0, 0)
else
print "Unknown MQTT command"
end
end
end
-- Parse MQTT data and extract color
-- @param data MQTT information
-- @param row string of the row e.g. "row1" used to publish current state
function parseBgColor(data, row)
local red=nil
local green=nil
local blue=nil
if (data:sub(1,1) == "#") then
red = tonumber(data:sub(2,3), 16)
green = tonumber(data:sub(4,5), 16)
blue = tonumber(data:sub(6,7), 16)
else
red, green, blue = string.match(data, "(%d+),(%d+),(%d+)")
end
if ((red ~= nil) and (green ~= nil) and (blue ~= nil) ) then
m:publish(mqttPrefix .. "/"..row, tostring(red) .. "," .. tostring(green) .. "," .. tostring(blue), 0, 0)
return string.char(green * briPercent / 100, red * briPercent / 100, blue * briPercent / 100)
else
return nil
end
end
function readTemp()
if (t ~= nil) then
addrs=t.addrs()
-- Total DS18B20 numbers
sensors=table.getn(addrs)
local temp1=0
if (sensors >= 1) then
temp1=t.read(addrs[0])
end
return temp1
else
return nil
end
end
-- MQTT extension
function registerMqtt()
m = mqtt.Client("wordclock", 120)
-- on publish message receive event
m:on("message", function(client, topic, data)
print(topic .. ":" )
if data ~= nil then
print(data)
if (topic == (mqttPrefix .. "/cmd/single")) then
handleSingleCommand(client, topic, data)
else
-- Handle here the /cmd/# sublevel
if (string.match(topic, "telnet$")) then
client:publish(mqttPrefix .. "/telnet", tostring(wifi.sta.getip()), 0, 0)
ws2812.write(string.char(0,0,0):rep(114))
print("Stop Mqtt and Temp")
m=nil
t=nil
mqttConnected = false
for i=0,6,1 do tmr.stop(i) end
collectgarbage()
mydofile("telnet")
if (startTelnetServer ~= nil) then
startTelnetServer()
end
else
for i=1,10,1 do
if (string.match(topic, "row".. tostring(i) .."$")) then
rowbgColor[i] = parseBgColor(data, "row" .. tostring(i))
print("Updated row" .. tostring(i) )
return
end
end
end
end
end
end)
m:connect(mqttServer, 1883, 0, function(client)
print("MQTT is connected")
mqttConnected = true
-- subscribe topic with qos = 0
client:subscribe(mqttPrefix .. "/cmd/#", 0)
tmr.alarm(3, 1000, 0, function()
-- publish a message with data = hello, QoS = 0, retain = 0
client:publish(mqttPrefix .. "/ip", tostring(wifi.sta.getip()), 0, 0)
local red = string.byte(colorBg,2)
local green = string.byte(colorBg,1)
local blue = string.byte(colorBg,3)
client:publish(mqttPrefix .. "/background", tostring(red) .. "," .. tostring(green) .. "," .. tostring(blue), 0, 0)
end)
end,
function(client, reason)
print("failed reason: " .. reason)
mqttConnected = false
end)
end
function startMqttClient()
if (mqttServer ~= nil and mqttPrefix ~= nil) then
registerMqtt()
print "Started MQTT client"
if (file.open("ds18b20.lc")) then
t=require("ds18b20")
t.setup(2) -- GPIO4
readTemp() -- read once, to setup chip
print "Setup temperature"
end
oldBrightness=0
oldTemp=0
tmr.alarm(5, 5001, 1 ,function()
if (mqttConnected) then
local temp = nil
if (t ~= nil) then
temp=readTemp()
print(tostring(temp) .. "°C")
end
if (oldBrightness ~= briPercent) then
m:publish(mqttPrefix .. "/brightness", tostring(briPercent), 0, 0)
elseif (temp ~= nil and temp ~= oldTemp) then
oldTemp = temp
m:publish(mqttPrefix .. "/temp", tostring(temp/100).."."..tostring(temp%100), 0, 0)
else
m:publish(mqttPrefix .. "/heap", tostring(node.heap()), 0, 0)
end
oldBrightness = briPercent
end
end)
end
end

Binary file not shown.

Binary file not shown.

View File

@ -1,16 +1,20 @@
# Firmware was compiled with the following modules: # Firmware was compiled with the following modules:
* LUA_USE_BUILTIN_STRING // for string.xxx() * LUA_USE_MODULES_ADC
* LUA_USE_BUILTIN_TABLE // for table.xxx() * LUA_USE_MODULES_BIT
* LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx() * LUA_USE_MODULES_DHT
* LUA_USE_BUILTIN_MATH // for math.xxx(), partially work
* LUA_USE_BUILTIN_DEBUG_MINIMAL // for debug.getregistry() and debug.traceback()
* LUA_USE_MODULES_FILE * LUA_USE_MODULES_FILE
* LUA_USE_MODULES_GPIO * LUA_USE_MODULES_GPIO
* LUA_USE_MODULES_I2C
* LUA_USE_MODULES_MQTT
* LUA_USE_MODULES_NET * LUA_USE_MODULES_NET
* LUA_USE_MODULES_NODE * LUA_USE_MODULES_NODE
* LUA_USE_MODULES_OW
* LUA_USE_MODULES_RTCFIFO
* LUA_USE_MODULES_RTCMEM
* LUA_USE_MODULES_RTCTIME * LUA_USE_MODULES_RTCTIME
* LUA_USE_MODULES_SNTP * LUA_USE_MODULES_SNTP
* LUA_USE_MODULES_SPI
* LUA_USE_MODULES_TMR * LUA_USE_MODULES_TMR
* LUA_USE_MODULES_UART * LUA_USE_MODULES_UART
* LUA_USE_MODULES_WIFI * LUA_USE_MODULES_WIFI

1
os/blank.bin Normal file
View File

@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ if [ $# -ne 1 ]; then
echo "One parameter required: the device of the serial interface" echo "One parameter required: the device of the serial interface"
echo "$0 <device>" echo "$0 <device>"
echo "e.g.:" echo "e.g.:"
echo "$0 ttyUSB0" echo "$0 /dev/ttyUSB0"
exit 1 exit 1
fi fi
@ -14,8 +14,8 @@ DEVICE=$1
# check the serial connection # check the serial connection
if [ ! -c /dev/$DEVICE ]; then if [ ! -c $DEVICE ]; then
echo "/dev/$DEVICE does not exist" echo "$DEVICE does not exist"
exit 1 exit 1
fi fi
@ -24,13 +24,12 @@ if [ ! -f esptool.py ]; then
echo "esptool.py" echo "esptool.py"
exit 1 exit 1
fi fi
python3 esptool.py --port $DEVICE $BAUD read_mac
./esptool.py --port /dev/$DEVICE $BAUD read_mac
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Error reading the MAC -> set the device into the bootloader!" echo "Error reading the MAC -> set the device into the bootloader!"
exit 1 exit 1
fi fi
echo "Flashing the new"
#./esptool.py --port /dev/$DEVICE $BAUD write_flash 0x00000 nodemcu-master-enduser_setup,file,gpio,net,node,rtcfifo,rtcmem,rtctime,sntp,spi,tmr,uart,wifi,ws2812-integer.bin #python3 esptool.py --port $DEVICE $BAUD write_flash -fm dio 0x00000 nodemcu2.bin
./esptool.py --port /dev/ttyUSB0 $BAUD write_flash 0x00000 0x00000.bin 0x10000 0x10000.bin python3 esptool.py --port $DEVICE write_flash -fm dio 0x00000 0x00000.bin 0x10000 0x10000.bin 0x3fc000 esp_init_data_default.bin

BIN
os/nodemcu2.bin Normal file

Binary file not shown.

8
simulation/.classpath Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="libs/luaj-jme-3.0.1.jar" sourcepath="libs/luaj-3.0.1/src"/>
<classpathentry kind="lib" path="libs/luaj-jse-3.0.1.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

1
simulation/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/bin/

17
simulation/.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>WS2812Emulation</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

12
simulation/Readme.md Normal file
View File

@ -0,0 +1,12 @@
# Simulation
The simualation should be started with the following arguments at this position:
`../init.lua ws28128ClockLayout.txt config.lua`
# Use it without Eclipse
Compiling:
`javac -d bin/ -cp libs/luaj-jme-3.0.1.jar:libs/luaj-jse-3.0.1.jar $(find src -name '*.java')`
Running:
`java -cp libs/luaj-jme-3.0.1.jar:libs/luaj-jse-3.0.1.jar:bin de.c3ma.ollo.WS2812Simulation ../init.lua ws28128ClockLayout.txt config.lua`

13
simulation/config.lua Normal file
View File

@ -0,0 +1,13 @@
green2=200
red=128
blue=200
color=string.char(0, 0, blue)
color1=string.char(red, 0, 0)
color2=string.char(tonumber(red*0.8), 0, 0)
color3=string.char(0, tonumber(green2*0.4), 0)
color4=string.char(0,0 ,tonumber(blue*0.2))
colorBg=string.char(0,0,0) -- black is the default background color
sntpserverhostname="ptbtime1.ptb.de"
timezoneoffset=1

3
simulation/libs/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
luaj-3.0.1.zip
luaj-3.0.1/
luaj-sources-3.0.1.jar

View File

@ -0,0 +1,7 @@
# Dependencies
The following file is expected here:
`luaj-3.0.1.zip`
It can be downloaded here:
https://sourceforge.net/projects/luaj/files/latest/download

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,17 @@
package de.c3ma.ollo;
/**
* created at 29.12.2017 - 18:29:07<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public interface LuaSimulation {
public void rebootTriggered();
public void setSimulationTime(long timeInMillis);
public void setADC(int value);
}

View File

@ -0,0 +1,54 @@
package de.c3ma.ollo;
import org.luaj.vm2.LuaValue;
/**
* created at 29.12.2017 - 18:48:27<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class LuaThreadTmr extends Thread {
private boolean running = true;
private boolean stopped = false;
private LuaValue code;
private final int delay;
private final int timerNumber;
public LuaThreadTmr(int timerNumber, LuaValue code, boolean endlessloop, int delay) {
this.code = code;
this.running = endlessloop;
this.delay = delay;
this.timerNumber = timerNumber;
}
@Override
public void run() {
try {
do {
Thread.sleep(delay);
if (code != null) {
code.call();
}
} while(running);
} catch(InterruptedException ie) {
System.err.println("[TMR] Timer" + timerNumber + " interrupted");
}
stopped = true;
}
public boolean isStopped() { return stopped; }
public void stopThread() {
running = false;
code = null;
}
}

View File

@ -0,0 +1,187 @@
package de.c3ma.ollo;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import javax.swing.SwingUtilities;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.JsePlatform;
import de.c3ma.ollo.mockup.DoFileFunction;
import de.c3ma.ollo.mockup.ESP8266Adc;
import de.c3ma.ollo.mockup.ESP8266File;
import de.c3ma.ollo.mockup.ESP8266Net;
import de.c3ma.ollo.mockup.ESP8266Node;
import de.c3ma.ollo.mockup.ESP8266Time;
import de.c3ma.ollo.mockup.ESP8266Tmr;
import de.c3ma.ollo.mockup.ESP8266Uart;
import de.c3ma.ollo.mockup.ESP8266Wifi;
import de.c3ma.ollo.mockup.ESP8266Ws2812;
/**
* created at 28.12.2017 - 13:19:32<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
*
* @author ollo<br />
*/
public class WS2812Simulation implements LuaSimulation {
private Globals globals = JsePlatform.standardGlobals();
private ESP8266Tmr espTmr = new ESP8266Tmr();
private ESP8266File espFile = new ESP8266File();
private ESP8266Node espNode = new ESP8266Node(this);
private DoFileFunction doFile = new DoFileFunction(globals);
private ESP8266Ws2812 ws2812 = new ESP8266Ws2812();
private ESP8266Adc adc = new ESP8266Adc();
private String scriptName;
public WS2812Simulation(File sourceFolder) {
globals.load(new ESP8266Uart());
globals.load(ws2812);
globals.load(espTmr);
globals.load(espFile);
globals.load(espNode);
globals.load(adc);
globals.load(new ESP8266Wifi());
globals.load(new ESP8266Net());
globals.load(new ESP8266Time());
globals.set("dofile", doFile);
try {
File tempFile = File.createTempFile("NodemcuSimuFile", "");
File tempDir = new File(tempFile.getParent() + File.separator + "Nodemcu" + System.currentTimeMillis());
tempDir.mkdir();
System.out.println("[Nodemcu] Directory is " + tempDir.getAbsolutePath());
// Copy all files into the temporary folder
for (File f : sourceFolder.listFiles()) {
Files.copy(f.toPath(), new File(tempDir.getAbsolutePath() + File.separator + f.getName()).toPath());
}
espFile.setWorkingDirectory(tempDir);
espNode.setWorkingDirectory(tempDir);
doFile.setWorkingDirectory(tempDir);
} catch (IOException e) {
System.err.println("[Nodemcu] " + e.getMessage());
espFile = null;
espNode = null;
}
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (args.length == 0) {
printUsage();
return;
}
if (args.length >= 1) {
File f = new File(args[0]);
if (f.exists()) {
WS2812Simulation simu = new WS2812Simulation(f.getParentFile());
System.out.println("File : " + f.getAbsolutePath());
if (args.length >= 2) {
simu.setWS2812Layout(new File(args[1]));
}
try {
if (args.length >= 3) {
File additionalFile = new File(args[2]);
if (additionalFile.exists() && (simu.doFile != null)) {
File targetFile = new File(simu.doFile.getWorkingDirectory()
+ File.separator + additionalFile.getName());
if (targetFile.exists()) {
if (targetFile.delete()) {
System.out.println("Removed original " + targetFile.getName() + "");
} else {
System.err.println("Cannot removed original " + targetFile.getName() + "");
}
}
Files.copy(additionalFile.toPath(), targetFile.toPath());
System.out.println("Integrate " + additionalFile.getName() + " into simulation");
} else {
System.err.println("Script " + args[2] + " cannot be found");
printUsage();
System.exit(1);
}
}
if (args.length >= 4) {
try {
ESP8266Tmr.gTimingFactor = Integer.parseInt(args[3]);
} catch (NumberFormatException nfe) {
System.err.println("Timing factor not parsable: " + nfe.getMessage());
printUsage();
}
}
simu.callScript(f.getName());
} catch (IOException e) {
System.err.println("[Nodemcu] " + e.getMessage());
}
}
} else {
printUsage();
}
}
});
}
private void setWS2812Layout(File file) {
if (file.exists()) {
ws2812.setLayout(file, this);
} else {
throw new RuntimeException("WS2812 Layout: " + file.getAbsolutePath() + " does not exists");
}
}
private static void printUsage() {
System.out.println("Usage:");
System.out.println("one argument required: file to execute.");
System.out.println(".e.g: init.lua (ws2812 layout configuration) (additional LUA script) (timing speedup factor)");
}
@Override
public void rebootTriggered() {
System.out.println("=================== Reboot in Simulation -> call it again =================");
this.espTmr.stopAllTimer();
try {
Thread.sleep(200);
if (this.scriptName != null) {
System.out.println("Reexecuting...");
callScript(this.scriptName);
}
} catch (InterruptedException e) {
}
}
private void callScript(String filename) {
this.scriptName = filename;
if ((espFile != null) && (espFile.getFileInWorkingDir(filename) != null)) {
LuaValue chunk = globals.loadfile(espFile.getFileInWorkingDir(filename).getAbsolutePath());
chunk.call();
} else {
throw new RuntimeException("Copy into temporary folder failed; script not available");
}
}
@Override
public void setSimulationTime(long timeInMillis) {
ESP8266Time.setOverwrittenTime(timeInMillis);
}
@Override
public void setADC(int value) {
adc.setADC(value);
}
}

View File

@ -0,0 +1,52 @@
package de.c3ma.ollo.mockup;
import java.io.File;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
/**
* created at 29.12.2017 - 20:23:48<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class DoFileFunction extends OneArgFunction {
private File workingDir = null;
private Globals globals;
public DoFileFunction(Globals globals) {
this.globals = globals;
}
@Override
public LuaValue call(LuaValue luaFilename) {
String filename = luaFilename.checkjstring();
File f = new File(workingDir.getAbsolutePath() + File.separator + filename);
if (f.exists()) {
LuaValue chunk = this.globals.loadfile(f.getAbsolutePath());
chunk.call();
return LuaValue.valueOf(true);
} else {
return LuaValue.valueOf(false);
}
}
public void setWorkingDirectory(File workingDir) {
this.workingDir = workingDir;
}
public String getWorkingDirectory() {
if (workingDir != null) {
return workingDir.getAbsolutePath();
} else {
return null;
}
}
}

View File

@ -0,0 +1,47 @@
package de.c3ma.ollo.mockup;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
/**
* created at 24.04.2019 - 21:12:03<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266Adc extends TwoArgFunction {
private int mAdc = 0;
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable adc = new LuaTable();
adc.set("read", new Read(this));
env.set("adc", adc);
env.get("package").get("loaded").set("adc", adc);
return adc;
}
private class Read extends VarArgFunction {
private ESP8266Adc adc;
public Read(ESP8266Adc a) {
this.adc = a;
}
public Varargs invoke(Varargs varargs) {
return LuaValue.valueOf(this.adc.mAdc);
}
}
public void setADC(int newValue) {
this.mAdc = newValue;
System.out.println("[ADC] updated to " + this.mAdc);
}
}

View File

@ -0,0 +1,101 @@
package de.c3ma.ollo.mockup;
import java.io.File;
import java.util.ArrayList;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
/**
* created at 29.12.2017 - 01:08:53<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266File extends TwoArgFunction {
private File workingDir = null;
private File openedFile = null;
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable file = new LuaTable();
file.set("open", new OpenFunction());
file.set("list", new ListFunction());
file.set("remove", new RemoveFunction());
env.set("file", file);
env.get("package").get("loaded").set("file", file);
return file;
}
private class ListFunction extends TwoArgFunction {
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) {
final LuaTable fileList = new LuaTable();
if ((workingDir != null) && (workingDir.exists())) {
File[] files = workingDir.listFiles();
for (File file : files) {
fileList.set(file.getName(), file.getAbsolutePath());
}
}
return fileList;
}
}
private class OpenFunction extends OneArgFunction {
@Override
public LuaValue call(LuaValue fileName) {
final String codeFileName = fileName.checkjstring();
final File f = new File( workingDir.getAbsolutePath() + File.separator + codeFileName);
//System.out.println("[FILE] Loading " + codeFileName);
if (f.exists()) {
ESP8266File.this.openedFile = f;
}
return LuaValue.valueOf((f.exists()));
}
}
private class RemoveFunction extends OneArgFunction {
@Override
public LuaValue call(LuaValue fileName) {
final String luaFileName = fileName.checkjstring();
System.out.println("[FILE] Removing " + luaFileName);
File f = new File(workingDir.getAbsolutePath() + File.separator + fileName);
if (f.exists()) {
return LuaValue.valueOf(f.delete());
} else {
return LuaValue.valueOf(false);
}
}
}
public void setWorkingDirectory(File workingDir) {
this.workingDir = workingDir;
}
public File getFileInWorkingDir(String filename) {
File f = new File (workingDir.getAbsolutePath() + File.separator + filename);
if (f.exists()) {
return f;
} else {
return null;
}
}
}

View File

@ -0,0 +1,83 @@
package de.c3ma.ollo.mockup;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
import de.c3ma.ollo.LuaSimulation;
/**
* created at 29.12.2017 - 01:29:40<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266Net extends TwoArgFunction {
public static final int PORTNUMBER_OFFSET = 4000;
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable net = new LuaTable();
net.set("createServer", new CreateServerFunction());
//FIXME net.set("send", new SendFunction());
net.set("TCP", "TCP");
env.set("net", net);
env.get("package").get("loaded").set("net", net);
return net;
}
private class CreateServerFunction extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
final LuaTable srv = new LuaTable();
srv.set("listen", new ListenFunction());
return srv;
}
}
private class ListenFunction extends TwoArgFunction {
@Override
public LuaValue call(LuaValue port, LuaValue function) {
int portnumber = port.checkint();
LuaFunction onListenFunction = function.checkfunction();
System.out.println("[Net] listening " + portnumber + "(simulating at " + (PORTNUMBER_OFFSET+ portnumber) + ")");
try
{
ServerSocket serverSocket = new ServerSocket(PORTNUMBER_OFFSET+portnumber);
serverSocket.setSoTimeout( 60000 ); // Timeout after one minute
Socket client = serverSocket.accept();
}
catch ( InterruptedIOException iioe )
{
System.err.println( "Timeout nach einer Minute!" );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("[Net] server running");
return LuaValue.valueOf(true);
}
}
}

View File

@ -0,0 +1,79 @@
package de.c3ma.ollo.mockup;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
import de.c3ma.ollo.LuaSimulation;
/**
* created at 29.12.2017 - 01:29:40<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266Node extends TwoArgFunction {
private File workingDir = null;
private LuaSimulation nodemcuSimu;
public ESP8266Node(LuaSimulation nodemcuSimu) {
this.nodemcuSimu = nodemcuSimu;
}
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable node = new LuaTable();
node.set("compile", new CompileFunction());
node.set("restart", new RestartFunction());
env.set("node", node);
env.get("package").get("loaded").set("node", node);
return node;
}
private class CompileFunction extends OneArgFunction {
@Override
public LuaValue call(LuaValue fileName) {
final String codeFileName = fileName.checkjstring();
final String compiledFileName = fileName.checkjstring().replace(".lua", ".lc");
final File f = new File( workingDir.getAbsolutePath() + File.separator + codeFileName);
System.out.println("[Node] Compiling " + compiledFileName);
final File outf = new File( workingDir.getAbsolutePath() + File.separator + compiledFileName);
if (f.exists()) {
//Simply copy the file as .lc file
try {
Files.copy(f.toPath(), outf.toPath());
} catch (IOException e) {
return LuaValue.valueOf(false);
}
}
return LuaValue.valueOf(f.exists());
}
}
private class RestartFunction extends ZeroArgFunction {
@Override
public LuaValue call() {
System.out.println("[Node] Restart");
nodemcuSimu.rebootTriggered();
return LuaValue.valueOf(false);
}
}
public void setWorkingDirectory(File workingDir) {
this.workingDir = workingDir;
}
}

View File

@ -0,0 +1,102 @@
package de.c3ma.ollo.mockup;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
/**
* created at 29.12.2017 - 00:07:22<br />
* creator: ollo<br />
* project: Time Emulation<br />
*
* Simulating the following modules:
* Sntp
* rtctime
*
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266Time extends TwoArgFunction {
private static long gSimulationStartTime = 0;
private static long gOverwrittenTime = 0;
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable sntp = new LuaTable();
sntp.set("sync", new SyncFunction());
env.set("sntp", sntp);
final LuaTable rtctime = new LuaTable();
rtctime.set("get", new GetFunction());
env.set("rtctime", rtctime);
env.get("package").get("loaded").set("sntp", sntp);
env.get("package").get("loaded").set("rtctime", rtctime);
return sntp;
}
/**
* Generate a time. If there is no speedup, it is simply the current system time.
* Otherwise the time is speedup by the given factor
* @return
*/
private long generateCurrenttime() {
if (gSimulationStartTime == 0) {
gSimulationStartTime = System.currentTimeMillis();
}
if (gOverwrittenTime == 0) {
/* Time simulation is disabled -> calculate something according to the speedup factor */
long time = System.currentTimeMillis();
if (ESP8266Tmr.gTimingFactor > 1) {
time = gSimulationStartTime + ((time - gSimulationStartTime) * ESP8266Tmr.gTimingFactor);
}
return time;
} else {
return gOverwrittenTime;
}
}
private class SyncFunction extends ThreeArgFunction {
@Override
public LuaValue call(LuaValue server, LuaValue callbackSuccess, LuaValue callbackFailure) {
String serverName = server.checkjstring();
LuaFunction cb = callbackSuccess.checkfunction();
System.out.println("[SNTP] sync " + serverName);
/*FIXME also make it possible to simulate the time */
long time = generateCurrenttime();
int seconds = (int) (time / 1000);
int useconds = (int) (time % 1000);
cb.call(LuaValue.valueOf(seconds), LuaValue.valueOf(useconds), LuaValue.valueOf(serverName));
return LuaValue.valueOf(true);
}
}
private class GetFunction extends ZeroArgFunction {
@Override
public LuaValue call() {
LuaValue[] v = new LuaValue[2];
/*FIXME also make it possible to simulate the time */
long time = generateCurrenttime();
int seconds = (int) (time / 1000);
int useconds = (int) (time % 1000);
v[0] = LuaValue.valueOf(seconds);
v[1] = LuaValue.valueOf(useconds);
return LuaValue.varargsOf(v).arg1();
}
}
public static void setOverwrittenTime(long timeInMillis) {
gOverwrittenTime = timeInMillis;
}
}

View File

@ -0,0 +1,95 @@
package de.c3ma.ollo.mockup;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
import de.c3ma.ollo.LuaThreadTmr;
/**
* created at 29.12.2017 - 00:07:22<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266Tmr extends TwoArgFunction {
private static final int MAXTHREADS = 7;
private static LuaThreadTmr[] allThreads = new LuaThreadTmr[MAXTHREADS];
public static int gTimingFactor = 1;
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable tmr = new LuaTable();
tmr.set("stop", new stop());
tmr.set("alarm", new alarm());
env.set("tmr", tmr);
env.get("package").get("loaded").set("tmr", tmr);
/* initialize the Threads */
for (Thread t : allThreads) {
t = null;
}
return tmr;
}
private boolean stopTmr(int i) {
if (allThreads[i] != null) {
allThreads[i].stopThread();
allThreads[i] = null;
return true;
} else {
return false;
}
}
private class stop extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
final int timerNumer = arg.toint();
System.out.println("[TMR] Timer" + timerNumer + " stopped");
return LuaValue.valueOf(stopTmr(timerNumer));
}
}
private class alarm extends VarArgFunction {
public Varargs invoke(Varargs varargs) {
if (varargs.narg()== 4) {
final int timerNumer = varargs.arg(1).toint();
final byte endlessloop = varargs.arg(3).tobyte();
final int delay = varargs.arg(2).toint();
final LuaValue code = varargs.arg(4);
System.out.println("[TMR] Timer" + timerNumer );
if ((timerNumer < 0) || (timerNumer > timerNumer)) {
return LuaValue.error("[TMR] Timer" + timerNumer + " is not available, choose 0 to 6");
}
if (stopTmr(timerNumer)) {
System.err.println("[TMR] Timer" + timerNumer + " stopped");
}
/* The cycletime is at least 1 ms */
allThreads[timerNumer] = new LuaThreadTmr(timerNumer, code, (endlessloop == 1), Math.max(delay / gTimingFactor, 1));
allThreads[timerNumer].start();
}
return LuaValue.valueOf(true);
}
}
public void stopAllTimer() {
for (int i = 0; i < allThreads.length; i++) {
stopTmr(i);
}
}
}

View File

@ -0,0 +1,38 @@
package de.c3ma.ollo.mockup;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;
/**
* created at 28.12.2017 - 23:05:05<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266Uart extends TwoArgFunction {
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable uart = new LuaTable();
uart.set("setup", new setup());
env.set("uart", uart);
env.get("package").get("loaded").set("uart", uart);
return uart;
}
private class setup extends VarArgFunction {
public Varargs invoke(Varargs varargs) {
if (varargs.narg()== 6) {
System.out.println("[UART] " + varargs.arg(2) + " " + varargs.arg(3)
+ ((varargs.arg(4).checkint() > 0) ? "Y" : "N")
+ varargs.arg(5));
}
return LuaValue.valueOf(true);
}
}
}

View File

@ -0,0 +1,81 @@
package de.c3ma.ollo.mockup;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
import de.c3ma.ollo.LuaSimulation;
/**
* created at 29.12.2017 - 01:29:40<br />
* creator: ollo<br />
* project: WifiEmulation<br />
* $Id: $<br />
* @author ollo<br />
*/
public class ESP8266Wifi extends TwoArgFunction {
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable wifi = new LuaTable();
wifi.set("setmode", new SetModeFunction());
final LuaTable ap = new LuaTable();
ap.set("config", new ConfigFunction());
wifi.set("ap", ap);
final LuaTable sta = new LuaTable();
sta.set("status", new StatusFunction());
sta.set("getip", new GetIpFunction());
wifi.set("sta", sta);
wifi.set("SOFTAP", "SOFTAP");
wifi.set("STATION", "STATION");
env.set("wifi", wifi);
env.get("package").get("loaded").set("wifi", wifi);
return wifi;
}
private class SetModeFunction extends OneArgFunction {
@Override
public LuaValue call(LuaValue apmode) {
final String APmodeString = apmode.checkjstring();
System.out.println("[Wifi] set mode " + APmodeString);
return LuaValue.valueOf(true);
}
}
private class ConfigFunction extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
System.out.println("[Wifi] config");
return LuaValue.valueOf(true);
}
}
private class StatusFunction extends ZeroArgFunction {
@Override
public LuaValue call() {
return LuaValue.valueOf(5);
}
}
private class GetIpFunction extends ZeroArgFunction {
@Override
public LuaValue call() {
return LuaValue.valueOf("127.0.0.1");
}
}
}

View File

@ -0,0 +1,88 @@
package de.c3ma.ollo.mockup;
import java.io.File;
import javax.swing.SwingUtilities;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
import de.c3ma.ollo.LuaSimulation;
import de.c3ma.ollo.mockup.ui.WS2812Layout;
/**
* created at 28.12.2017 - 23:34:04<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
*
* @author ollo<br />
*/
public class ESP8266Ws2812 extends TwoArgFunction {
private static WS2812Layout layout = null;
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
env.checkglobals();
final LuaTable ws2812 = new LuaTable();
ws2812.set("init", new init());
ws2812.set("write", new write());
env.set("ws2812", ws2812);
env.get("package").get("loaded").set("ws2812", ws2812);
return ws2812;
}
private class init extends ZeroArgFunction {
@Override
public LuaValue call() {
System.out.println("[WS2812] init");
return LuaValue.valueOf(true);
}
}
private class write extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
if (arg.isstring()) {
LuaString jstring = arg.checkstring();
final int length = jstring.rawlen();
if ((length % 3) == 0) {
final byte[] array = jstring.m_bytes;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
for (int i = 0; i < length; i += 3) {
if (ESP8266Ws2812.layout != null) {
int r = array[i + 0]+(Byte.MIN_VALUE*-1);
int b = array[i + 1]+(Byte.MIN_VALUE*-1);
int g = array[i + 2]+(Byte.MIN_VALUE*-1);
ESP8266Ws2812.layout.updateLED(i / 3, r, g, b);
}
}
}
});
}
if (ESP8266Ws2812.layout == null) {
System.out.println("[WS2812] write length:" + length);
} else {
}
}
return LuaValue.valueOf(true);
}
}
public void setLayout(File file, LuaSimulation nodemcuSimu) {
if (ESP8266Ws2812.layout == null) {
ESP8266Ws2812.layout = WS2812Layout.parse(file, nodemcuSimu);
}
}
}

View File

@ -0,0 +1,323 @@
package de.c3ma.ollo.mockup.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import de.c3ma.ollo.LuaSimulation;
/**
* created at 02.01.2018 - 12:57:02<br />
* creator: ollo<br />
* project: WS2812Emulation<br />
* $Id: $<br />
*
* @author ollo<br />
*/
public class WS2812Layout extends JFrame {
/**
*
*/
private static final long serialVersionUID = -6815557232118826140L;
/**
* Parameter for the ADC brightness control
*/
private static final int ADC_INIT = 512;
private static final int ADC_MIN = 0;
private static final int ADC_MAX = 1024;
private ArrayList<String> mLines = new ArrayList<String>();
private int mColumn = 0;
private int mRow = 0;
private Element[][] mElements;
private LuaSimulation nodemcuSimu;
public WS2812Layout(LuaSimulation nodemcuSimu) {
this.nodemcuSimu = nodemcuSimu;
}
public static WS2812Layout parse(File file, LuaSimulation nodemcuSimu) {
WS2812Layout layout = null;
try {
BufferedReader br = new BufferedReader(new FileReader(file));
try {
String line = br.readLine();
if (line != null) {
layout = new WS2812Layout(nodemcuSimu);
}
while (line != null) {
if (!line.startsWith("#")) {
layout.mLines.add(line);
layout.mRow++;
layout.mColumn = Math.max(layout.mColumn, line.length());
}
/* get the next line */
line = br.readLine();
}
/* parse each line */
layout.parse();
layout.createAndDisplayGUI();
} finally {
if (br != null) {
br.close();
}
}
} catch (IOException ioe) {
}
return layout;
}
private void createAndDisplayGUI() {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
contentPane.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 2));
JPanel ledPanel = new JPanel();
ledPanel.setBackground(Color.BLACK);
ledPanel.setLayout(new GridLayout(this.mRow, this.mColumn, 10, 10));
for (int i = 0; i < this.mRow; i++) {
for (int j = 0; j < this.mColumn; j++) {
if (this.mElements[i][j] != null) {
ledPanel.add(this.mElements[i][j]);
}
}
}
contentPane.add(ledPanel, BorderLayout.CENTER);
JSlider adc = new JSlider(JSlider.VERTICAL,
ADC_MIN, ADC_MAX, ADC_INIT);
adc.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
nodemcuSimu.setADC(adc.getValue());
}
});
contentPane.add(adc, BorderLayout.EAST);
JPanel bottomPanel = new JPanel();
final JTextField dateTime = new JTextField("yyyy-mm-dd HH:MM:SS");
dateTime.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
warn();
}
public void removeUpdate(DocumentEvent e) {
warn();
}
public void insertUpdate(DocumentEvent e) {
warn();
}
public void warn() {
final String pattern = "(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})";
final String current = dateTime.getText();
if (current.length() <=0) {
/* color "nothing" green */
dateTime.setForeground(Color.GREEN);
/* disable the time simulation */
nodemcuSimu.setSimulationTime(0);
return;
}
if (!current.matches(pattern)) {
dateTime.setForeground(Color.RED);
} else {
dateTime.setForeground(Color.BLACK);
Pattern dateTimePattern = Pattern.compile(pattern);
Matcher matcher = dateTimePattern.matcher(current);
int year=0;
int month=0;
int day=0;
int hour=0;
int minute=0;
int second=0;
matcher.find();
for (int g = 1; g <= matcher.groupCount(); g++) {
switch(g) {
case 1: /* Year */
year = Integer.parseInt(matcher.group(g));
break;
case 2: /* Month */
month = Integer.parseInt(matcher.group(g));
break;
case 3: /* Day */
day = Integer.parseInt(matcher.group(g));
break;
case 4: /* Hour */
hour = Integer.parseInt(matcher.group(g));
break;
case 5: /* Minute */
minute = Integer.parseInt(matcher.group(g));
break;
case 6: /* Second */
second = Integer.parseInt(matcher.group(g));
break;
}
}
System.out.println("[GUI] Set time to: " + year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second);
GregorianCalendar gc = new GregorianCalendar(year, month, day, hour, minute, second);
nodemcuSimu.setSimulationTime(gc.getTimeInMillis());
}
}
});
bottomPanel.add(dateTime);
final JButton btnSetCurrentTime = new JButton("Set time");
btnSetCurrentTime.setActionCommand("Set time");
btnSetCurrentTime.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JButton but = (JButton) ae.getSource();
if (but.equals(btnSetCurrentTime)) {
GregorianCalendar gc = new GregorianCalendar();
dateTime.setText(String.format("%d-%02d-%02d %02d:%02d:%02d",
gc.get(Calendar.YEAR), gc.get(Calendar.MONTH), gc.get(Calendar.DAY_OF_MONTH),
gc.get(Calendar.HOUR_OF_DAY), gc.get(Calendar.MINUTE), gc.get(Calendar.SECOND)));
}
}
});
bottomPanel.add(btnSetCurrentTime);
final JButton btnReboot = new JButton("Reboot");
btnReboot.setActionCommand("Reboot simulation");
btnReboot.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JButton but = (JButton) ae.getSource();
if (but.equals(btnReboot)) {
System.out.println("[Node] Restart");
nodemcuSimu.rebootTriggered();
}
}
});
bottomPanel.add(btnReboot);
contentPane.add(bottomPanel, BorderLayout.SOUTH);
setContentPane(contentPane);
pack();
setLocationByPlatform(true);
setVisible(true);
}
private void parse() {
this.mElements = new Element[this.mRow][this.mColumn];
int row = 0;
for (String line : this.mLines) {
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if ((('A' <= c) && (c <= 'Z')) || (('0' <= c) && (c <= '9')) || (c == 'Ä') || (c == 'Ö')
|| (c == 'Ü')) {
this.mElements[row][i] = new Element(c);
} else {
this.mElements[row][i] = new Element();
}
this.mElements[row][i].setColor(0, 0, 0);
}
row++;
}
}
public class Element extends JLabel {
/**
*
*/
private static final long serialVersionUID = -3933903441113933637L;
private boolean noText = false;
/**
* Draw a simple rect
*/
public Element() {
super();
this.noText = true;
this.setBackground(Color.BLACK);
}
/**
* Draw a character
*
* @param character
* to show
*/
public Element(char character) {
super("" + character);
setFont(new Font("Dialog", Font.BOLD, 24));
setHorizontalAlignment(CENTER);
// FIXME: Background color is not updated:
this.setBackground(Color.BLACK);
}
public void setColor(int red, int green, int blue) {
this.setForeground(new Color(red, green, blue));
this.repaint();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (noText) {
sb.append(" ");
} else {
sb.append("" + this.getText());
}
sb.append("|" + Integer.toHexString(this.getForeground().getRed()) + " "
+ Integer.toHexString(this.getForeground().getGreen()) + " "
+ Integer.toHexString(this.getForeground().getBlue()));
return sb.toString();
}
}
public void updateLED(int index, int r, int g, int b) {
if (mElements != null) {
int i = (index / mColumn);
int j = (index % mColumn);
// Swap each second row
if (i % 2 == 1) {
j = (mColumn-1) - j;
}
if ((i < mElements.length) && (j < mElements[i].length) && (mElements[i][j] != null)) {
Element curlbl = mElements[i][j];
curlbl.setColor(r, g, b);
}
}
}
}

View File

@ -0,0 +1,17 @@
# This file describes the layout of the WS2812 LEDs.
# _ will only draw a rect
# A-Z or 1-9 will draw the text or number
# each element will be updated with its color with the known commands of the nodemcu firmware
#
# Here the configuration for the wordclock:
ESKISTLFÜNF
ZEHNZWANZIG
DREIVIERTEL
TGNACHVORJM
HALBXZWÖLFP
ZWEINSIEBEN
KDREIRHFÜNF
ELFNEUNVIER
WACHTZEHNRS
BSECHSFMUHR
____

36
telnet.lua Normal file
View File

@ -0,0 +1,36 @@
-- Telnet Server
function startTelnetServer()
s=net.createServer(net.TCP, 180)
s:listen(23,function(c)
global_c=c
printlines = {}
function s_output(str)
if(global_c~=nil) then
if #printlines > 0 then
printlines[ #printlines + 1] = str
else
printlines[ #printlines + 1] = str
global_c:send("\r") -- Send something, so the queue is read after sending
end
end
end
node.output(s_output, 0)
c:on("receive",function(c,l)
node.input(l)
end)
c:on("disconnection",function(c)
node.output(nil)
global_c=nil
end)
c:on("sent", function()
if #printlines > 0 then
global_c:send(table.remove(printlines, 1))
end
end)
print("Welcome to the Wordclock.")
print("- mydofile(\"commands\")")
print("- storeConfig()")
print("Visite https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en for further commands")
end)
print("Telnetserver is up")
end

View File

@ -1,2 +1,4 @@
# Source: # luatool.py
**Not** supported with python 3.x
## Source:
https://github.com/4refr0nt/luatool/tree/master/luatool https://github.com/4refr0nt/luatool/tree/master/luatool

View File

@ -3,6 +3,14 @@
LUATOOL=./tools/luatool.py LUATOOL=./tools/luatool.py
DEVICE=$1 DEVICE=$1
BAUD=115200
# check environment
if [ ! -f $LUATOOL ]; then
echo "$LUATOOL not found"
echo "is the command prompt at the same level as the tools folder ?"
exit 1
fi
# check the serial connection # check the serial connection
@ -11,23 +19,38 @@ if [ ! -c $DEVICE ]; then
exit 1 exit 1
fi fi
if [ $# -eq 0 ]; then
if [ $# -ne 1 ]; then
echo "" echo ""
echo "e.g. usage $0 <device>" echo "e.g. usage $0 <device> [<files to upoad>]"
exit 1 exit 1
fi fi
FILES="displayword.lua main.lua timecore.lua webpage.lua webserver.lua wordclock.lua init.lua" if [ $# -eq 1 ]; then
FILES="displayword.lua main.lua timecore.lua webpage.html webserver.lua telnet.lua wordclock.lua init.lua"
else
FILES=$2
fi
# Format filesystem first # Format filesystem first
echo "Format the complete ESP" echo "Format the complete ESP"
$LUATOOL -p $DEVICE -w $LUATOOL -p $DEVICE -w -b $BAUD
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "STOOOOP" echo "STOOOOP"
exit 1 exit 1
fi fi
echo #stty -F $DEVICE $BAUD
#echo "Reboot the ESP"
#echo "node.restart()" >> $DEVICE
#sleep 1
#for i in $(seq 0 5); do
# echo "Stop TMR $i"
# echo "tmr.stop($i)" >> $DEVICE
# sleep 1
#done
#echo
echo "Start Flasing ..." echo "Start Flasing ..."
for f in $FILES; do for f in $FILES; do
if [ ! -f $f ]; then if [ ! -f $f ]; then
@ -36,7 +59,7 @@ for f in $FILES; do
exit 1 exit 1
fi fi
echo "------------- $f ------------" echo "------------- $f ------------"
$LUATOOL -p $DEVICE -f $f -t $f $LUATOOL -p $DEVICE -f $f -b $BAUD -t $f
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "STOOOOP" echo "STOOOOP"
exit 1 exit 1
@ -44,6 +67,6 @@ for f in $FILES; do
done done
echo "Reboot the ESP" echo "Reboot the ESP"
$LUATOOL -p $DEVICE -r echo "node.restart()" >> $DEVICE
exit 0 exit 0

56
tools/remoteFlash.sh Executable file
View File

@ -0,0 +1,56 @@
#!/bin/bash
IP=$1
FLASHTOOL=./tools/tcpFlash.py
if [ ! -f $FLASHTOOL ]; then
echo "Execute the script in root folder of the project"
exit 2
fi
if [ "$IP" == "" ]; then
echo "IP address of ESP required"
echo "usage:"
echo "$0 <IP>"
echo "$0 192.168.0.2"
exit 1
fi
# check the connection
echo "Searching $IP ..."
ping $IP -c 2 >> /dev/null
if [ $? -ne 0 ]; then
echo "Entered IP address: $IP is NOT online"
exit 2
fi
echo "Upgrading $IP"
echo "stopWordclock()" > /tmp/wordClockCMD.txt
echo "uart.write(0, tostring(node.heap())" >> /tmp/wordClockCMD.txt
echo "c = string.char(0,0,128)" >> /tmp/wordClockCMD.txt
echo "w = string.char(0,0,0)" >> /tmp/wordClockCMD.txt
echo "ws2812.write(w:rep(4) .. c .. w:rep(15) .. c .. w:rep(9) .. c .. w:rep(30) .. c .. w:rep(41) .. c )" >> /tmp/wordClockCMD.txt
$FLASHTOOL -f /tmp/wordClockCMD.txt -t $IP -v
FILES="displayword.lua main.lua timecore.lua webpage.html webserver.lua wordclock.lua init.lua"
echo "Start Flasing ..."
for f in $FILES; do
if [ ! -f $f ]; then
echo "Cannot find $f"
echo "place the terminal into the folder where the lua files are present"
exit 1
fi
echo "------------- $f ------------"
$FLASHTOOL -t $IP -f $f
if [ $? -ne 0 ]; then
echo "STOOOOP"
exit 1
fi
done
echo "TODO: Reboot the ESP"
#echo "node.restart()" | nc $IP 80
exit 0

145
tools/tcpFlash.py Executable file
View File

@ -0,0 +1,145 @@
#!/usr/bin/python
import argparse
import socket
import os.path
import sys #for exit and flushing of stdout
import time
def sendRecv(s, message, answer):
msg = message + "\n"
s.sendall(msg)
reply = s.recv(4096)
i=1
while ((not (answer in reply)) and (i < 10)):
reply += s.recv(4096)
i = i + 1
if answer not in reply:
return False
else:
return True
def sendCmd(s, message, cleaningEnter=False):
msg = message + "\n"
s.sendall(msg)
time.sleep(0.050)
reply = s.recv(4096)
i=1
while ((not (">" in reply)) and (i < 10)):
time.sleep((0.050) * i)
reply += s.recv(4096)
i = i + 1
# print "Send\t" + message
# print "Got\t" + reply
if (cleaningEnter):
s.sendall("\n")
if "stdin:1:" in reply:
print "ERROR, received : " + reply
return False
elif ">" in reply:
return True
else:
print "ERROR, received : " + reply
return False
def main(nodeip, luafile, volatile=None):
if ( not os.path.isfile(luafile) ):
print "The file " + luafile + " is not available"
else:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((nodeip, 23))
time.sleep(0.050)
s.sendall("\n")
# Receive the hello Message of answer of the ESP
if (not sendRecv(s, "\n", "Welcome to ") ):
print "Cannot connect to the ESP"
s.close()
sys.exit(2)
# Read all lines from the welcome message
i=0
reply = s.recv(4096)
while ((reply is not None) and (not (">" in reply)) and (i < 100)):
reply = s.recv(4096)
i = i + 1
# Communication tests
if ( not sendRecv(s, "print(12345)", "12345") ):
print "NOT communicating with an ESP8266 running LUA (nodemcu) firmware"
s.close()
sys.exit(3)
sendCmd(s, "for i=0,5 do tmr.stop(i) end")
sendCmd(s, "collectgarbage()")
if (volatile is None):
print "Flashing " + luafile
sendCmd(s, "file.remove(\"" + luafile+"\");", True)
sendCmd(s, "w= file.writeline", True)
sendCmd(s, "file.open(\"" + luafile + "\",\"w+\");", True)
else:
print "Executing " + luafile + " on nodemcu"
with open(luafile) as f:
contents = f.readlines()
i=1
for line in contents:
print "\rSending " + str(i) + "/" + str(len(contents)) + " ...",
sys.stdout.flush()
l = line.rstrip()
if ( l.endswith("]") ):
l = l + " "
print "add a space at the end"
if (volatile is None):
if (not sendCmd(s, "w([==[" + l + "]==]);")):
print "Cannot write line " + str(i)
s.close()
sys.exit(4)
else:
if (not sendCmd(s, l)):
print "Cannot write line " + str(i)
s.close()
sys.exit(4)
i=i+1
if (volatile is None):
# Finished with updating the file in LUA
if (not sendCmd(s, "w([[" + "--EOF" + "]]);")):
print "Cannot write line " + "-- EOF"
if (not sendCmd(s, "file.close();")):
print "Cannot close the file"
sys.exit(4)
# Check if the file exists:
if (not sendRecv(s, "=file.exists(\"" + luafile + "\")", "true")):
print("Cannot send " + luafile + " to the ESP")
sys.exit(4)
else:
print("Updated " + luafile + " successfully")
else:
print("Send " + luafile + " successfully")
# Cleaning the socket by closing it
s.close()
sys.exit(0) # Report that the flashing was succesfull
except socket.error, msg:
print 'Failed to create socket. Error code: ' + str(msg[0]) + ' , Error message : ' + msg[1]
sys.exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-t', '--target', help='IP address or dns of the ESP to flash')
parser.add_argument('-f', '--file', help='LUA file, that should be updated')
parser.add_argument('-v', '--volatile', help='File is executed at the commandline', action='store_const', const=1)
args = parser.parse_args()
if (args.target and args.file and args.volatile):
main(args.target, args.file, args.volatile)
elif (args.target and args.file):
main(args.target, args.file)
else:
parser.print_help()

View File

@ -56,7 +56,6 @@ expected.itis=1
expected.one=1 expected.one=1
expected.clock=1 expected.clock=1
checkWords(leds, expected, 1, 0) checkWords(leds, expected, 1, 0)
checkCharacter(display_countwords_de(leds), 11)
leds=display_timestat(2,5) leds=display_timestat(2,5)
expected={} expected={}
@ -64,7 +63,6 @@ expected.two=1
expected.fiveMin=1 expected.fiveMin=1
expected.after=1 expected.after=1
checkWords(leds, expected, 2 , 5) checkWords(leds, expected, 2 , 5)
checkCharacter(display_countwords_de(leds), 12)
leds=display_timestat(3,10) leds=display_timestat(3,10)
expected={} expected={}
@ -72,7 +70,6 @@ expected.three=1
expected.tenMin=1 expected.tenMin=1
expected.after=1 expected.after=1
checkWords(leds, expected, 3 , 10) checkWords(leds, expected, 3 , 10)
checkCharacter(display_countwords_de(leds), 12)
leds=display_timestat(4,15) leds=display_timestat(4,15)
expected={} expected={}
@ -80,7 +77,6 @@ expected.four=1
expected.after=1 expected.after=1
expected.quater=1 expected.quater=1
checkWords(leds, expected, 4 , 15) checkWords(leds, expected, 4 , 15)
checkCharacter(display_countwords_de(leds), 15)
leds=display_timestat(5,20) leds=display_timestat(5,20)
expected={} expected={}
@ -88,7 +84,6 @@ expected.five=1
expected.twenty=1 expected.twenty=1
expected.after=1 expected.after=1
checkWords(leds, expected, 5 , 20) checkWords(leds, expected, 5 , 20)
checkCharacter(display_countwords_de(leds), 15)
leds=display_timestat(6,25) leds=display_timestat(6,25)
expected={} expected={}
@ -97,7 +92,6 @@ expected.fiveMin=1
expected.before=1 expected.before=1
expected.half=1 expected.half=1
checkWords(leds, expected, 6 , 25) checkWords(leds, expected, 6 , 25)
checkCharacter(display_countwords_de(leds), 17)
leds=display_timestat(7,30) leds=display_timestat(7,30)
expected={} expected={}
@ -105,7 +99,6 @@ expected.itis=1
expected.eight=1 expected.eight=1
expected.half=1 expected.half=1
checkWords(leds, expected, 7 , 30) checkWords(leds, expected, 7 , 30)
checkCharacter(display_countwords_de(leds), 13)
leds=display_timestat(8,35) leds=display_timestat(8,35)
expected={} expected={}
@ -114,7 +107,6 @@ expected.half=1
expected.fiveMin=1 expected.fiveMin=1
expected.after=1 expected.after=1
checkWords(leds, expected, 8 , 35) checkWords(leds, expected, 8 , 35)
checkCharacter(display_countwords_de(leds), 16)
leds=display_timestat(9,40) leds=display_timestat(9,40)
expected={} expected={}
@ -122,7 +114,6 @@ expected.ten=1
expected.twenty=1 expected.twenty=1
expected.before=1 expected.before=1
checkWords(leds, expected, 9 , 40) checkWords(leds, expected, 9 , 40)
checkCharacter(display_countwords_de(leds), 14)
leds=display_timestat(10,45) leds=display_timestat(10,45)
expected={} expected={}
@ -130,7 +121,6 @@ expected.eleven=1
expected.quater=1 expected.quater=1
expected.before=1 expected.before=1
checkWords(leds, expected, 10 , 45) checkWords(leds, expected, 10 , 45)
checkCharacter(display_countwords_de(leds), 13)
leds=display_timestat(11,50) leds=display_timestat(11,50)
expected={} expected={}
@ -138,7 +128,6 @@ expected.twelve=1
expected.tenMin=1 expected.tenMin=1
expected.before=1 expected.before=1
checkWords(leds, expected, 11 , 50) checkWords(leds, expected, 11 , 50)
checkCharacter(display_countwords_de(leds), 12)
leds=display_timestat(12,55) leds=display_timestat(12,55)
expected={} expected={}
@ -146,7 +135,6 @@ expected.oneLong=1
expected.fiveMin=1 expected.fiveMin=1
expected.before=1 expected.before=1
checkWords(leds, expected, 12 , 55) checkWords(leds, expected, 12 , 55)
checkCharacter(display_countwords_de(leds), 11)
leds=display_timestat(13,00) leds=display_timestat(13,00)
expected={} expected={}
@ -154,7 +142,6 @@ expected.itis=1
expected.one=1 expected.one=1
expected.clock=1 expected.clock=1
checkWords(leds, expected, 13 , 00) checkWords(leds, expected, 13 , 00)
checkCharacter(display_countwords_de(leds), 11)
-- test the minutes inbetween -- test the minutes inbetween
leds=display_timestat(14,01) leds=display_timestat(14,01)
@ -164,7 +151,6 @@ expected.two=1
expected.min1=1 expected.min1=1
expected.clock=1 expected.clock=1
checkWords(leds, expected, 14 , 01) checkWords(leds, expected, 14 , 01)
checkCharacter(display_countwords_de(leds), 12)
leds=display_timestat(15,02) leds=display_timestat(15,02)
expected={} expected={}
@ -173,7 +159,6 @@ expected.three=1
expected.min2=1 expected.min2=1
expected.clock=1 expected.clock=1
checkWords(leds, expected, 15 , 02) checkWords(leds, expected, 15 , 02)
checkCharacter(display_countwords_de(leds), 12)
leds=display_timestat(16,03) leds=display_timestat(16,03)
expected={} expected={}
@ -182,7 +167,6 @@ expected.four=1
expected.min3=1 expected.min3=1
expected.clock=1 expected.clock=1
checkWords(leds, expected, 16 , 03) checkWords(leds, expected, 16 , 03)
checkCharacter(display_countwords_de(leds), 12)
leds=display_timestat(17,04) leds=display_timestat(17,04)
expected={} expected={}
@ -191,7 +175,6 @@ expected.five=1
expected.min4=1 expected.min4=1
expected.clock=1 expected.clock=1
checkWords(leds, expected, 17 , 04) checkWords(leds, expected, 17 , 04)
checkCharacter(display_countwords_de(leds), 12)
leds=display_timestat(18,06) leds=display_timestat(18,06)
expected={} expected={}
@ -200,7 +183,6 @@ expected.after=1
expected.min1=1 expected.min1=1
expected.six=1 expected.six=1
checkWords(leds, expected, 18 , 06) checkWords(leds, expected, 18 , 06)
checkCharacter(display_countwords_de(leds), 13)
leds=display_timestat(19,09) leds=display_timestat(19,09)
expected={} expected={}
@ -209,7 +191,6 @@ expected.after=1
expected.min4=1 expected.min4=1
expected.seven=1 expected.seven=1
checkWords(leds, expected, 19 , 09) checkWords(leds, expected, 19 , 09)
checkCharacter(display_countwords_de(leds), 14)
leds=display_timestat(20,17) leds=display_timestat(20,17)
expected={} expected={}
@ -218,7 +199,6 @@ expected.after=1
expected.min2=1 expected.min2=1
expected.eight=1 expected.eight=1
checkWords(leds, expected, 20 , 17) checkWords(leds, expected, 20 , 17)
checkCharacter(display_countwords_de(leds), 15)

View File

@ -45,16 +45,9 @@ Please note that all settings are mandatory<br /><br />
<table id="table-6"> <table id="table-6">
<tr><th><b>WIFI-SSID</b></th><td><input id="ssid" name="ssid" value="$SSID"></td><td>SSID of the wireless network</td></tr> <tr><th><b>WIFI-SSID</b></th><td><input id="ssid" name="ssid" value="$SSID"></td><td>SSID of the wireless network</td></tr>
<tr><th>WIFI-Password</th><td><input id="password" name="password"></td><td>Password of the wireless network</td></tr> <tr><th>WIFI-Password</th><td><input id="password" name="password"></td><td>Password of the wireless network</td></tr>
<tr><th>SNTP Server</th><td><input id="sntpserver" name="sntpserver" value="$SNTPSERVER"></td><td>Server to sync the time with. Only one ntp server is allowed.</tr> <tr><th>SNTP Server</th><td><input id="sntpserver" name="sntpserver" value="$SNTPSERVER"></td><td>Server to sync the time with. Only one ntp server is allowed.</td></tr>
<tr><th>Offset to UTC time</th><td><input id="timezoneoffset" name="timezoneoffset" value="$TIMEOFFSET"></td><td>Define the offset to UTC time in hours. For example +1 hour for Germany</tr> <tr><th>Offset to UTC time</th><td><input id="timezoneoffset" name="timezoneoffset" value="$TIMEOFFSET"></td><td>Define the offset to UTC time in hours. For example +1 hour for Germany</td></tr>
<tr><th>Foreground Color</th><td><input type="color" name="fcolor" value="$HEXCOLORFG"></td><td>LED Color for all minutes, divisible by five</td></tr> <tr><th>Foreground Color</th><td><input type="color" name="fcolor" value="$HEXCOLORFG"></td><td>LED Color for all minutes, divisible by five</td></tr>
<tr><th>Background Color</th><td><input type="color" name="bcolor" value="$HEXCOLORBG"></td><td>Background LED Color</td></tr>
<tr><th>1. Minute Color</th><td><input type="color" name="colorMin1" value="$HEXCOLOR1"></td><td>First minute after</td></tr>
<tr><th>2. Minute Color</th><td><input type="color" name="colorMin2" value="$HEXCOLOR2"></td><td>Second minute after</td></tr>
<tr><th>3. Minute Color</th><td><input type="color" name="colorMin3" value="$HEXCOLOR3"></td><td>Third minute after</td></tr>
<tr><th>4. Minute Color</th><td><input type="color" name="colorMin4" value="$HEXCOLOR4"></td><td>Fourth minute after</td></tr>
<tr><th>Three quater</th><td><input type="checkbox" name="threequater" $THREEQUATER></td><td>Dreiviertel Joa/nei</td></tr>
<tr><td colspan="3"><div align="center"><input type="submit" value="Save Configuration" onclick="this.value='Submitting ..';this.disabled='disabled'; this.form.submit();"></div></td></tr> <tr><td colspan="3"><div align="center"><input type="submit" value="Save Configuration" onclick="this.value='Submitting ..';this.disabled='disabled'; this.form.submit();"></div></td></tr>
<tr><td colspan="3"><div align="center"><input type="submit" name="action" value="Reboot"></div></td></tr> <tr><td colspan="3"><div align="center"><input type="submit" name="action" value="Reboot"></div></td></tr>
</table> </table>

View File

@ -1,7 +1,7 @@
--TODO: --TODO:
configFile="config.lua" configFile="config.lua"
httpSending=false
sentBytes=0 sentBytes=0
function sendPage(conn, nameOfFile, replaceMap) function sendPage(conn, nameOfFile, replaceMap)
collectgarbage() collectgarbage()
@ -11,8 +11,9 @@ function sendPage(conn, nameOfFile, replaceMap)
conn:close() conn:close()
print("Page sent") print("Page sent")
collectgarbage() collectgarbage()
httpSending=false
else else
print("Next") collectgarbage()
sendPage(conn, nameOfFile, replaceMap) sendPage(conn, nameOfFile, replaceMap)
end end
end) end)
@ -32,9 +33,6 @@ function sendPage(conn, nameOfFile, replaceMap)
local line = file.readline() local line = file.readline()
while (line ~= nil) do while (line ~= nil) do
-- increase the amount of sent bytes
sentBytes=sentBytes+string.len(line)
-- all placeholder begin with a $, so search for it in the current line -- all placeholder begin with a $, so search for it in the current line
if (line:find("$") ~= nil) then if (line:find("$") ~= nil) then
-- Replace the placeholder with the dynamic content -- Replace the placeholder with the dynamic content
@ -45,10 +43,15 @@ function sendPage(conn, nameOfFile, replaceMap)
end end
end end
end end
-- increase the amount of sent bytes
sentBytes=sentBytes+string.len(line)
buf = buf .. line buf = buf .. line
-- Sent after 1k data -- Sent after 500 bytes data
if (string.len(buf) >= 700) then if ( (string.len(buf) >= 500) or (node.heap() < 2000) ) then
line=nil line=nil
conn:send(buf) conn:send(buf)
print("Sent part of " .. sentBytes .. "B") print("Sent part of " .. sentBytes .. "B")
@ -62,10 +65,11 @@ function sendPage(conn, nameOfFile, replaceMap)
--reset amount of sent bytes, as we reached the end --reset amount of sent bytes, as we reached the end
sentBytes=0 sentBytes=0
-- send the rest -- send the rest
if (string.len(buf) > 0) then
conn:send(buf) conn:send(buf)
print("Sent rest") print("Sent rest")
end end
end
end end
function fillDynamicMap() function fillDynamicMap()
@ -118,24 +122,61 @@ function fillDynamicMap()
replaceMap["$HEXCOLOR3"]=hexColor3 replaceMap["$HEXCOLOR3"]=hexColor3
replaceMap["$HEXCOLOR4"]=hexColor4 replaceMap["$HEXCOLOR4"]=hexColor4
replaceMap["$HEXCOLORBG"]=hexColorBg replaceMap["$HEXCOLORBG"]=hexColorBg
replaceMap["$INV46"]=((inv46 ~= nil and inv46 == "on") and "checked" or "")
replaceMap["$AUTODIM"]=((dim ~= nil and dim == "on") and "checked" or "")
return replaceMap return replaceMap
end end
function stopWordclock()
print("Stop all Wordclock")
-- Stop all
for i=0,5 do tmr.stop(i) end
-- unload all other functions
-- grep function *.lua | grep -v webserver | grep -v init.lua | grep -v main.lua | cut -f 2 -d ':' | grep "^function" | sed "s/function //g" | grep -o "^[a-zA-Z0-9\_]*"
updateColor = nil
drawLEDs = nil
round = nil
generateLEDs = nil
isSummerTime = nil
getUTCtime = nil
getTime = nil
display_timestat = nil
collectgarbage()
end
function startWebServer() function startWebServer()
srv=net.createServer(net.TCP) srv=net.createServer(net.TCP)
srv:listen(80,function(conn) srv:listen(80,function(conn)
conn:on("receive", function(conn,payload) conn:on("receive", function(conn,payload)
if (httpSending) then
print("HTTP sending... be patient!")
return
end
if (payload:find("GET /") ~= nil) then if (payload:find("GET /") ~= nil) then
--here is code for handling http request from a web-browser httpSending=true
stopWordclock()
if (color == nil) then
color=string.char(0,128,0)
end
ws2812.write(string.char(0,0,0):rep(56) .. color:rep(2) .. string.char(0,0,0):rep(4) .. color:rep(2) .. string.char(0,0,0):rep(48))
-- Start Time after 3 minute
tmr.alarm(5, 180000, 0 ,function()
dependModules = { "timecore" , "wordclock", "displayword" }
for _,mod in pairs(dependModules) do
print("Loading " .. mod)
mydofile(mod)
end
-- Start the time Thread again
tmr.alarm(1, 20000, 1 ,function()
displayTime()
end)
end)
if (sendPage ~= nil) then if (sendPage ~= nil) then
print("Sending webpage.html ...") print("Sending webpage.html (" .. tostring(node.heap()) .. "B free) ...")
-- Load the sendPagewebcontent -- Load the sendPagewebcontent
replaceMap=fillDynamicMap() replaceMap=fillDynamicMap()
sendPage(conn, "webpage.html", replaceMap) sendPage(conn, "webpage.html", replaceMap)
end end
else if (payload:find("POST /") ~=nil) then else if (payload:find("POST /") ~=nil) then
--code for handling the POST-request (updating settings) --code for handling the POST-request (updating settings)
_, postdatastart = payload:find("\r\n\r\n") _, postdatastart = payload:find("\r\n\r\n")
@ -165,7 +206,9 @@ function startWebServer()
file.remove(configFile .. ".new") file.remove(configFile .. ".new")
sec, _ = rtctime.get() sec, _ = rtctime.get()
file.open(configFile.. ".new", "w+") file.open(configFile.. ".new", "w+")
file.write("-- Config\n" .. "wifi.sta.config(\"" .. _POST.ssid .. "\",[[" .. _POST.password .. "]])\n" .. "sntpserverhostname=\"" .. _POST.sntpserver .. "\"\n" .. "timezoneoffset=\"" .. _POST.timezoneoffset .. "\"\n") file.write("-- Config\n" .. "station_cfg={}\nstation_cfg.ssid=\"" .. _POST.ssid .. "\"\nstation_cfg.pwd=\"" .. _POST.password .. "\"\nstation_cfg.save=false\nwifi.sta.config(station_cfg)\n")
file.write("sntpserverhostname=\"" .. _POST.sntpserver .. "\"\n" .. "timezoneoffset=\"" .. _POST.timezoneoffset .. "\"\n".. "inv46=\"" .. tostring(_POST.inv46) .. "\"\n" .. "dim=\"" .. tostring(_POST.dim) .. "\"\n")
if ( _POST.fcolor ~= nil) then if ( _POST.fcolor ~= nil) then
-- color=string.char(_POST.green, _POST.red, _POST.blue) -- color=string.char(_POST.green, _POST.red, _POST.blue)
print ("Got fcolor: " .. _POST.fcolor) print ("Got fcolor: " .. _POST.fcolor)
@ -278,18 +321,17 @@ function startWebServer()
global_c=nil global_c=nil
end) end)
print("Welcome to Word Clock") print("Welcome to Word Clock")
end end
end end
end) end)
conn:on("disconnection", function(c) conn:on("disconnection", function(c)
print("Goodbye") print("Goodbye")
node.output(nil) -- un-register the redirect output function, output goes to serial node.output(nil) -- un-register the redirect output function, output goes to serial
collectgarbage()
--reset amount of sent bytes, as we reached the end --reset amount of sent bytes, as we reached the end
sentBytes=0 sentBytes=0
end) end)
end) end)
end end
--FileView done.

View File

@ -12,7 +12,7 @@ function display_timestat(hours, minutes, longmode)
end end
-- generate an empty return type -- generate an empty return type
local ret = { itis=0, fiveMin=0, tenMin=0, after=0, before=0, threeHour=0, quater=0, threequater=0, half=0, s=0, local ret = { it=0, is=0, fiveMin=0, tenMin=0, after=0, before=0, threeHour=0, quater=0, threequater=0, half=0, s=0,
one=0, oneLong=0, two=0, three=0, four=0, five=0, six=0, seven=0, eight=0, nine=0, ten=0, eleven=0, twelve=0, one=0, oneLong=0, two=0, three=0, four=0, five=0, six=0, seven=0, eight=0, nine=0, ten=0, eleven=0, twelve=0,
twenty=0, twenty=0,
clock=0, sr_nc=0, min1=0, min2=0, min3=0, min4=0 } clock=0, sr_nc=0, min1=0, min2=0, min3=0, min4=0 }
@ -31,7 +31,8 @@ function display_timestat(hours, minutes, longmode)
if ((longmode==1) if ((longmode==1)
or (minutes==0) or (minutes==0)
or (minutes==6)) then or (minutes==6)) then
ret.itis=1 ret.it=1
ret.is=1
end end
-- Handle minutes -- Handle minutes
@ -63,7 +64,7 @@ function display_timestat(hours, minutes, longmode)
ret.before=1 ret.before=1
elseif (minutes==9) then elseif (minutes==9) then
-- Hande if three quater or quater before is displayed -- Hande if three quater or quater before is displayed
if (threequater ~= nil) then if ((threequater ~= nil) and (threequater==true or threequater=="on")) then
ret.threequater=1 ret.threequater=1
else else
ret.quater = 1 ret.quater = 1
@ -104,10 +105,10 @@ function display_timestat(hours, minutes, longmode)
end end
if (hours == 1) then if (hours == 1) then
if (ret.before == 1) then if ((ret.it == 1) and (ret.half == 0) ) then
ret.oneLong = 1
else
ret.one=1 ret.one=1
else
ret.oneLong=1
end end
elseif (hours == 2) then elseif (hours == 2) then
ret.two=1 ret.two=1
@ -135,82 +136,3 @@ function display_timestat(hours, minutes, longmode)
collectgarbage() collectgarbage()
return ret return ret
end end
-- @fn display_countwords
-- Count the amount of characters, used to describe the current time in words
-- @param words the same structure, as generated with the function @see display_timestat
-- @return the amount of words, used to describe the time or <code>0</code> on errors
function display_countwords_de(words)
local amount=0
if (words.itis == 1) then
amount = amount + 5
end
if (words.fiveMin == 1) then
amount = amount + 4
end
if (words.tenMin == 1) then
amount = amount + 4
end
if (words.twenty == 1) then
amount = amount + 7
end
if (words.threequater == 1) then
amount = amount + 11
end
if (words.quater == 1) then
amount = amount + 7
end
if (words.before == 1) then
amount = amount + 3
end
if (words.after == 1) then
amount = amount + 4
end
if (words.half == 1) then
amount = amount + 4
end
if (words.twelve == 1) then
amount = amount + 5
end
if (words.seven == 1) then
amount = amount + 6
end
if (words.one == 1) then
amount = amount + 3
end
if (words.oneLong == 1) then
amount = amount + 4
end
if (words.two == 1) then
amount = amount + 4
end
if (words.three == 1) then
amount = amount + 4
end
if (words.five == 1) then
amount = amount + 4
end
if (words.four == 1) then
amount = amount + 4
end
if (words.nine == 1) then
amount = amount + 4
end
if (words.eleven == 1) then
amount = amount + 3
end
if (words.eight == 1) then
amount = amount + 4
end
if (words.ten == 1) then
amount = amount + 4
end
if (words.clock == 1) then
amount = amount + 3
end
if (words.six == 1) then
amount = amount + 5
end
return amount
end