NodeMCU external callback function generates error - esp8266

I am using NodeMCU 0.9.6 with an Adafruit HUZZAH ESP8266.
The following code, which defines the callback function within the http.get statement, works:
http.get("http://192.168.2.38/ICmd.py?i=esp8266001", nil, function(code, data)
if (code < 0) then
print("***ICmd HTTP request failed")
else
print("***ICmd callback: ", code, data)
end
end)
The following code moves the callback function definition from within the http.get statement to be a standalone function:
function chkICmdResp(code, data)
if (code < 0) then
print("***ICmd HTTP request failed")
else
print("***ICmd callback: ", code, data)
end
end
http.get("http://192.168.2.38/ICmd.py?i=esp8266001", nil, chkICmdResp(code, data) )
The second format does not work, it generates the error:
stdin:2: attempt to compare nil with number
stack traceback:
stdin:2: in function 'chkICmdResponse'
stdin:1: in main chunk
Am I doing something incorrectly (NodeMCU noobie)? Or is this a design feature or a bug?
It would be nice to be able to have a single callback function that can be referenced from multiple http.get calls.
Note: I have also tried this with the net.socket:on("receive",...) module and got the same error result when trying to use a function that is defined outside of the calling statement.

There's a fundamental mismatch between what you're trying to do and what your firmware offers. Do NOT use those old 0.9.x NodeMCU binaries. They're based on old Espressif SDKs, they're no longer maintained and they're full of bugs.
The HTTP client module has only been around for a few months. It's currently only available in the NodeMCU dev branch i.e. not even in current master. Recipe for success:
build a firmware from the dev branch that includes the HTTP module (off by default): http://nodemcu.readthedocs.org/en/dev/en/build/
flash the binary to your chip: http://nodemcu.readthedocs.org/en/dev/en/flash/
use the HTTP module as documented: http://nodemcu.readthedocs.org/en/dev/en/modules/http/#httpget
If you decide to stick with the net module rather than using HTTP there's a small example in the docs: http://nodemcu.readthedocs.org/en/dev/en/modules/net/#netsocketsend

Related

luaL_loadbufferx returns syntaxerrors

I'm working on a personal project and I'm trying to get Lua to work on my embedded device.
I have my own simple file system that works with the the flash drive, and now I'm trying to use modules for the lua scripts that I run on the device.
I have edited linit.c, to make it also load the modules that are existing in the flash drive, and it works for a few modules, but for most of them it just gives me a syntax error when it parses the contents of the module. I have a lua interpreter running on my Windows machine and the code I'm writing is syntactically correct and works, and the Lua API that I use is of the same version 5.4 on the device.
These are the arguments I pass to
luaL_loadbufferx(L, luaCFunction, sizeOfModule, moduleName, "t")
where, L is the lua state, luaCFunction is the lua module wrapped in a C-style return statement, sizeOfModule, moduleName and t is selfexplanatory.
Right now luaL_loadbufferx is called in a loop for every module in my flash-drive, I have overwritten the openf function from the Lua API for these external modules.
This below is one of the examples of a module that gives me
"Syntax Error: PANIC, unprotected error in call to Lua API
[string "module"]:3: '(' expected near 'writeobj'"
File: module.lua
Contents:
function writeobj()
print('Hello World')
end
File: run.lua
Contents:
require ('module')
writeobj()
Does anyone know why this happens or did I not provide sufficient information? Please let me know.
The problem was that I thought the modules passed to the buffer had to be of the LuaToC form, i.e. "return { ...luamodule...}", but changing it to pass the module only to loadbuffer was sufficient enough because it covers the case of it not being in a C style return format.

tmr.create() fails in dirt simple code

On an ESP8266, a LUA tmr.create() fails. Just a single line of code from the nodemcu docs. I've yet to get this working; stumped; have found nothing on the 'net here or forums. Posted in several other places with no replies. Odd
Basic node commands work. And, here's the thing that's getting me: some timer functions work - for instance tmr.alarm() does - but tmr.create() doesn't.
QUESTION 1: if the tmr module is loaded, isn't the entire module loaded, not just parts of it?
When I try this one line copied directly from the NodeMCU docs, it fails:
local mytimer = tmr.create()
with the usual message, I understand to mean an object is undefined:
stdin:1: attempt to call field 'create' (a nil value)
QUESTION 2: What am I missing or doing wrong?
Nodemcu's flasher successfully sent its default
INTERNAL://NODEMCU # 0x00000
NodeMCU 0.9.5 build 20150318
The board is an Adafruit Huzzah ESP8266, not a NodeMCU board. They both use the same ESP12, I think, so that shouldn't matter (or I hope it doesn't :-/)
NodeMCU 0.9.5 build 20150318
There's your culprit. That version is ancient. create() was added much later. Build a recent version from the master branch, https://nodemcu.readthedocs.io/en/master/en/build/ (note master in the URL), and flash that one.

"attempt to call global 'tonumber' (a nil value)" in Lua, embedded (in VLC)

I use VLC media player 1.1.9 on Ubuntu 11.04. I'm trying to experiment with lua extensions for VLC; so I've added the file test.lua in ~/.local/share/vlc/lua/extensions/, which has only these two lines:
fps="25.000"
frame_duration=1/tonumber(fps)
When I run vlc with verbose output for debugging, I get (edited to split on multiple lines:):
$ vlc --verbose 2
...
[0xa213874] lua generic warning: Error loading script
~/.local/share/vlc/lua/extensions/test.lua:
.../.local/share/vlc/lua/extensions/test.lua:2:
attempt to call global 'tonumber' (a nil value)
...
Now, as far as I know, tonumber as function is part of Lua5.1 proper (Lua 5.1 Reference Manual: tonumber) - and on my system:
$ locate --regex 'lua.*so.*' | head -4
/usr/lib/libipelua.so.7.0.10
/usr/lib/liblua5.1.so
/usr/lib/liblua5.1.so.0
/usr/lib/liblua5.1.so.0.0.0
... apparently I do have Lua 5.1 installed.
So, why do I get an error on using tonumber here - and how can I use this (and other) standard functions in a VLC lua extension properly?
Documentation is sparse for VLC Lua extensions to say the least but I did find an example in the github vlc repository here: https://github.com/videolan/vlc/blob/master/share/lua/extensions/VLSub.lua
Judging from that example it appears you need to supply some basic event functions for your addon for VLC to call into when certain events happen. Some of the obvious callback handlers I've noticed:
descriptor, this should return a table that contains fields describing your addon.
activate, this seems to get called when you activate it from view menubar.
deactivate, called when you deactivate the addon from view menubar.
plus a couple of other functions like close and input_change which you can guess what they're for.
From my brief testing done on VLC 2.0.8 under Win7 it appears VLC loads the lua extension using an empty sandbox environment. This is likely the reason you're getting nil for tonumber and I'm betting none of the other standard lua functions are accessible either when you try to perform computation at this global scope.
However, if I move that code into one of the event handling functions then all those standard functions are accessible again. For example:
function descriptor()
return
{
title = "Test Ext";
version = "0.1";
author = "";
shortdesc = "Testing Lua Extension";
capabilities = {};
description = "VLC Hello Test Addon";
}
end
function activate()
print "test activating"
local fps = tonumber "25.000"
local frame_duration = 1 / fps
print(frame_duration)
return true
end
-- ...
That prints out what you would expect in the console debug log. Now the documentation (what little there is) doesn't mention any of this but what's probably happening here is VLC is injecting the standard lua functions and vlc api table into the sandboxed environment when any of these event handlers get called. But during the extension loading phase, it is done in an empty sandbox environment which explains why all those lua function calls end up being nil when you try to use it at the outter most scope.
I recommend cloning the VLC source tree from github and then performing a grep on the C source that's embedding lua to see what VLC is really doing behind the scenes. Most of the relevant code will likely be here: https://github.com/videolan/vlc/tree/master/modules/lua
Probably some extension script installed in your system overwrites the function and the Lua interpreter instance is shared between all extension scripts, so you end up not being able to call the function if that script is called before yours.
As a quick workaround, Lua being dynamically typed, you can still do things like:
1 / "25.000"
and the string will be coerced to a number.
Alternatively, you can define a tonumber equivalent like:
string_to_num = function(s) return s + 0 end
This again relies on dynamic typing.

override assert in lua

my setup board uses lighttpd to provide web UI to the board.
It uses lua and JS to draw logic.
What I am seeing is If I enter an URL as "IPofboard/somejunkhere"; its properly throwing "404 not found"
But when I fire "IPofboard/somejunk.lp" (which is some junk lua file); It produces an "assert" error for file not found. Thats how lua works.
But I want to modify/override this assert to show same custom message as "404 not found"
any idea?
I am new to lua. Is it even doable?
As lhf mentions, it is very easy to redefine any function in Lua, but I think this may not be what you need. The issue is that after you do
local origAssert = assert
assert = function(message)
do something (possibly using origAssert)
end
then every function call that uses assert will use your new assert function, which is probably not what you want. Instead, you can call your function in "protected" mode: this will trap the assertion as an error message, and you can then decide what to do. For example,
ok, ret1, ret2 = pcall(yourFunction, arg1)
if not ok then
do something, possibly print ret1 (the error message)
end
Same thing if you are requiring a module that does some initialization:
ok, module = pcall(require, yourModuleName)
if not ok then
print("ERROR:", module) -- if not ok then module is err message
end
I'm not familiar with how lighttpd embeds Lua, but in Lua you can redefine anything, including functions from the standard Lua library, such as assert.

How can I write a plugin for VLC that responds to play, pause and stop events?

I'd like to write a very simple plugin for VLC that makes web requests when a media is played, paused, or stopped. It is a very similar to a scrobbling plugin .
I saw that VLC supports plugin and extensions (which are very simple Lua scripts) but I haven't been able to find any information on how to do this.
I guess I'd need to write a plugin that registers some callbacks -- am I right? Any idea on how I could accomplish this? It seems to be quite an uphill battle figuring this out. Can I do this using Python?
I'm using VLC 2.2.1 on Windows.
Here's a simple Lua plugin that recognizes play/pause/stop events:
function descriptor()
return {
title = "VLC Dummy Extension",
capabilities = { "playing-listener" }
}
end
function activate()
end
function deactivate()
end
function meta_changed()
end
function playing_changed()
vlc.msg.dbg("[Dummy] Status: " .. vlc.playlist.status())
end
Notes:
VLC will call activate(), deactivate(), meta_changed() at some point in the plugin life cycle. You're not required to include them, but VLC will fill up your debug log with useless "function not found" messages.
If the plugin's capabilities contain playing-listener, VLC will expect the playing_changed() hook and call it when appropriate (even though the code comment says the hook name is "status_changed").
vlc.playlist.status() returns "stopped", "playing", "paused" or "unknown".
Run:
Save the plugin in a .lua file, then drop it in VLC's extensions folder: %APPDATA%\vlc\lua\extensions\ (Windows) or ~/.local/share/vlc/lua/extensions/ (Linux).
Load it by Tools > Plugins and extensions, Reload extensions (restarting VLC is unnecessary).
Activate it (there's an option under View, named after title in the plugin descriptor); activate() will be called.
To view all logs (vlc.msg calls), open Tools > Messages (Ctrl+M), set the level to debug and filter by "lua".
To do something whenever a new item plays:
Add input-listener to the plugin's capabilities.
Add the corresponding hook input_changed().
Use vlc.input.item() to get the current item (name, URI, metadata, etc).
You can post what's playing to an HTTP server. VLC offers vlc.net, which means you have to program for sockets. Fortunately, the extension vlsub, shipped with VLC by default, has utility methods for sending GET requests. We can steal those.
In this example, I'm just sending a threadbare GET:
http://127.0.0.1:5000/?name=MMFR.2015.720p.mp4
If I don't care about redirects or reading responses, get() is a lot simpler than the vlsub's version:
function descriptor()
return {
title = "VLC Dummy Extension",
capabilities = { "input-listener" }
}
end
function activate()
end
function deactivate()
end
function meta_changed()
end
function input_changed()
if vlc.input.is_playing() and vlc.playlist.status() == "playing" then
local item = vlc.input.item()
if item then
vlc.msg.dbg("[Dummy] Now playing: " .. item:name())
get("http://127.0.0.1:5000/?name=" .. item:name())
end
end
end
function get(url)
local u = vlc.net.url_parse(url)
local host, port, path = u["host"], u["port"], u["path"]
local header = {
"GET "..path.." HTTP/1.1",
"Host: "..host,
"",
""
}
local request = table.concat(header, "\r\n")
http_req(host, port, request)
end
function http_req(host, port, request)
local fd = vlc.net.connect_tcp(host, port)
if not fd then return false end
local pollfds = {}
pollfds[fd] = vlc.net.POLLIN
vlc.net.send(fd, request)
vlc.net.poll(pollfds)
local chunk = vlc.net.recv(fd, 2048)
while chunk do
vlc.net.poll(pollfds)
chunk = vlc.net.recv(fd, 1024)
end
vlc.net.close(fd)
end
You have two options: write a lua module (check the MSN notification plugin for sample code) or write a C plugin (similar to the scrobbler you already found).
I'd suggest the lua way, since it's cross-platform compatible and it's a way easier language. Additionally, compiling C plugins for VLC is a real pain unless you use Linux or OS X.
Python is only supported for client apps on top of VLC at the moment, but we don't support its use internal to VLC.

Resources