How can I reset ESP8266 MicroPython after main.py crashes? - iot

I have a NodeMCU ESP8266 board running MicroPython. I'm running a web server on my ESP8266. This is my first IoT project based on one of these boards.
The below is a snippet of the code.
This is being executed within main.py. Every now and then, something causes the code to crash (perhaps timing and request based). When main.py exits, for whatever reason, I'm dropped back at the python CLI.
I'd like for the board to reset when this happens (if there isn't a better way).
What is the best method of restarting/reseting the ESP8266?
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(5)
print('listening on', addr)
while True:
cl, addr = s.accept()
print('client connected from', addr)
cl_file = cl.makefile('rwb', 0)
print("Request:")
while True:
line = cl_file.readline()
print("Line:" , line)
if not line or line == b'\r\n':
print("breaking")
break
if line == b'GET /active HTTP/1.1\r\n':

MicroPython has machine.reset() function to reset a board.
Python (not just MicroPython) uses exception handling to handle errors.
Combining the two, you can easily achieve what you want. For example:
a = 4
b = 2
try:
a / b
except:
machine.reset()
If in the code above you replace value of b with 0, your board will reset. If you think about it for a bit, you probably will find out that it doesn't make much sense - you don't want your board suddenly reset if you just divide by 0 by mistake or otherwise. There're got to be better ways to handle errors! Likewise, you may want to think about your own case and see if resetting the board is really the best choice. If you think that yes, that's fine, just always keep in mind that you programmed your board to suddenly reset. Otherwise, your next question here may be "My board suddenly resets! Why???" ;-)

It may be late for the original question, but the answer I am going to share might help other people. Consider it is not a final solution, but in many scenarios, it may save a day. You can explore your case.
The solution is using the internal scheduling function of MicroPython. since its execution is guaranteed, then its behavior can be used as a tool to mimic a functional watchdog.
Following code will run with given timers and threshold which can be customized in your case, and if the timer reaches its threshold, and the value of wd_buffer is not updated for that time, then the function might be called, and we repeat the process again.
So in order to prevent the ESP getting restarted in this case after 12 sec, you have to in someplace in your code, periodically (shorter than 12 sec or adjust the timer and threshold according to your need) update the value of the Global wd_buffer variable. Hope it helps.
# Simple WD - Global Variable
wd_feeder = 0
wd_buffer = 0
wd_counter = 0
wd_threshold = 4
def wd_checker(calledvalue):
print('watchdog is checking... feeder= {} buffer= {}'.format(wd_feeder, wd_buffer))
global wd_counter
global wd_buffer
global wd_feeder
if wd_feeder == wd_buffer:
print('state is suspicious ... counter is {} incrementing the counter'.format(wd_counter))
wd_counter += 1
else:
wd_counter = 0
wd_feeder = wd_buffer
if wd_counter == wd_threshold:
print('Counter is reached its threshold, following function will be called')
wd_feeder = wd_buffer = wd_counter = 0
machine.reset()
if __name__ == '__main__':
scheduler_wd = machine.Timer(-1)
scheduler_wd.init(period=3000, mode=machine.Timer.PERIODIC, callback=wd_checker)

you could add a while loop checking for the Flash Button (GPIO pin 0) like this:
import machine
pin = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
while pin.value():
print('Put your code here...')
print('..this will looping until the Flash button is pressed...')
print('...and then it continues here.')

You could execute your code (which should be outside of the main.py -> other file) from the boot or the main.py. if it drops out it should execute the following code, which could trigger a reset.
You may have to catch the error first.
I hope I helped

Related

With Lua/NodeMCU, how can I wait until >1 mqtt publish calls have been made before running a block of code?

My application involves a battery-powered ESP8266 running NodeMCU for the purpose of updating sensor values periodically over MQTT.
To save battery life, I want to call dsleep() as soon as I'm done with my work. That work might involve more than 1 call to mqqt.Client.publish(). This brings us to the problem I'm facing.
I'm a Lua newbie, but as I understand, the right way to run some code after publish() has finished is to give it a PUBACK callback:
m = mqtt.Client(...)
m.publish("/my/topic", "some message", 1, 0, callback_func)
And in a simple case like above, that works great - even though the actual sending of the MQTT message is async with regard to the publish() call (see a good discussion of this here), callback_func() in the above example will only be called when publish() is done.
But when I have more than 1 call to publish() and want my callback to a) be called after they're all complete, and b) only be called once, I'm stuck.
The naive approach to this would be to put the callback (which is optional) only on the Nth publish() call:
m = mqtt.Client(...)
m.publish("/my/topic", "some message", 1, 0)
m.publish("/another/topic", "unrelated message", 1, 0, callback_func)
But this will not do what's expected. As documented:
NOTE: When calling publish() more than once, the last callback function defined will be called for ALL publish commands.
So in the above example, callback_func() ends up getting called twice (once for each successful publish().
I could combine the multiple publish() calls into a single call, but that feels like an ugly hack, and would have other adverse implications. If my two messages are conceptually distinct, this approach would push logic to separate them into the subscriber - yuck. And if they needed to go to different topics, this would be even worse. There must be a better way.
I thought perhaps mqqt.Client.close() would wait for my different publish() calls to finish, but it doesn't.
I'm out of ideas, and hoping someone with more Lua and/or NodeMCU+mqqt experience can give me a nudge in the right direction.
Here's my actual code, if it helps:
-- prior to this, we've gotten on the wifi network and acquired an IP
dofile("temp.lua") -- provides get_temp()
m = mqtt.Client("clientid", 120, "8266test", "password")
function mainloop(client)
print("connected - at top of loop")
m:publish("uptime",tmr.time(),1,0, function(client) print("sent uptime") end)
temp, humi = get_temp()
if (temp ~= nil) then
print(string.format("temp: %d", temp))
print(string.format("humi: %d", humi))
m:publish("temp",temp,1,0)
m:publish("humi",humi,1,0, function(client) -- note: this callback will be used for all publish() calls
rtctime.dsleep(SLEEP_USEC)
end)
end
end
m:on("connect", mainloop)
m:on("offline", function(client) is_connected = false print ("offline") end)
m:connect(MQQT_SVR, 1883, 0, mainloop,
function(client, reason) print("failed reason: "..reason) end)
Option 1:
publish all data at once, then go to sleep.
Option 2:
split your callback into two parts. the first part checks if you are done, the second part goes to sleep if you are done.
Of course you can solve this differently, count how many are left, count how many you have sent, send and remove items from a list until the list is empty,...
Of course there are more options but these are simple and sufficient.
Edit: example as requested
local totalItemCount = 5
function publishCallback()
itemsPublished = (itemsPublished or 0) + 1
print("item published")
if itemsPublished == totalItemCount then
print("I'm done, good night!")
end
end
for i = 1, totalItemCount do
publishCallback()
end
item published
item published
item published
item published
item published
I'm done, good night!

NodeMCU WiFi auto connect

I am trying to resolve wifi connectivity using Lua language. I have been combing through the api to find a solution but nothing solid yet. I asked a previous question, dynamically switch between wifi networks and the answer did address the question in the way I asked it, but it didn't accomplish what I expected.
Basically, I have two different networks from two different providers. All I want the ESP8266 12e to do is detect when or if the current network has no internet access and automatically switch to the next network. It must continuously try to connect at say a 3 minute interval until it is successful and not just give up.
For testing purposes I tried this code below. The plan is to use the variable "effectiveRouter" and write some logic to switch based on the current router.
effectiveRouter = nil
function wifiConnect(id,pw)
counter = 0
wifi.sta.config(id,pw)
tmr.alarm(1, 1000, tmr.ALARM_SEMI, function()
counter = counter + 1
if counter < 10 then
if wifi.sta.getip() == nil then
print("NO IP yet! Trying on "..id)
tmr.start(1)
else
print("Connected, IP is "..wifi.sta.getip())
end
end
end)
end
wifiConnect("myNetwork","myPassword")
print(effectiveRouter)
When I run that code, I get effectiveRouter as nil on the console. This tells me that the print statement ran before the method call was complete, print(effectiveRouter). I am very very new to lua as this is my first time with the language. I am certain this boiler plate code must have been done before. Can someone please point me in the right direction? I am open to shifting to the arduino IDE as I already have it set up for the NodeMCU ESP8266. May be I can follow the logic better as I come from a java-OOP background.
I eventually sat down and tested my sketch from the previous answer. Two additional lines and we're good to go...
What I missed is that wifi.sta.config() resets the connection attempts if auto connect == true (which is the default). So, if you call it to connect to AP X while it's in the process of connecting to X it will start from scratch - and thus usually not get an IP before it's called again.
effectiveRouter = nil
counter = 0
wifi.sta.config("dlink", "password1")
tmr.alarm(1, 1000, tmr.ALARM_SEMI, function()
counter = counter + 1
if counter < 30 then
if wifi.sta.getip() == nil then
print("NO IP yet! Keep trying to connect to dlink")
tmr.start(1) -- restart
else
print("Connected to dlink, IP is "..wifi.sta.getip())
effectiveRouter = "dlink"
--startProgram()
end
elseif counter == 30 then
wifi.sta.config("cisco", "password2")
-- there should also be tmr.start(1) in here as suggested in the comment
elseif counter < 60 then
if wifi.sta.getip() == nil then
print("NO IP yet! Keep trying to connect to cisco")
tmr.start(1) -- restart
else
print("Connected to cisco, IP is "..wifi.sta.getip())
effectiveRouter = "cisco"
--startProgram()
end
else
print("Out of options, giving up.")
end
end)
You better to migrate a callback based architecture to be sure that you have successfully connected. Here is doc for it :
https://nodemcu.readthedocs.io/en/master/en/modules/wifi/#wifistaeventmonreg
You can listen for
wifi.STA_GOTIP
And make your custom operations in it. Do not forget to start eventmon.
P.s. I am not able to see your variable effectiveRouter in related function.

In Lua, using a boolean variable from another script in the same project ends up with nill value error

This code is for a modding engine, Unitale base on Unity Written in Lua
So I am trying to use a Boolean Variable in my script poseur.lua, so when certain conditions are met so I can pass it to the other script encounter.lua, where a engine Predefined functions is being uses to make actions happens base on the occurring moment.
I tried to read the engine documentation multiple times, follow the exact syntax of Lua's fonction like GetVar(), SetVar(), SetGobal(),GetGlobal().
Searching and google thing about the Language, post on the subreddit and Game Exchange and tried to solve it by myself for hours... I just can't do it and I can't understand why ?
I will show parts of my codes for each.
poseur:
-- A basic monster script skeleton you can copy and modify for your own creations.
comments = {"Smells like the work\rof an enemy stand.",
"Nidhogg_Warrior is posing like his\rlife depends on it.",
"Nidhogg_Warrior's limbs shouldn't\rbe moving in this way."}
commands = {"GREET", "JUMP", "FLIRT", "CRINGE"}
EndDialougue = {" ! ! !","ouiii"}
sprite = "poseur" --Always PNG. Extension is added automatically.
name = "Nidhogg_Warrior"
hp = 99
atk = 1
def = 1
check = "The Nidhogg_Warrior is\rsearching for the Nidhogg"
dialogbubble = "rightlarge" -- See documentation for what bubbles you have available.
canspare = false
cancheck = true
GreetCounter = 5
Berserk = false
encounter:
-- A basic encounter script skeleton you can copy and modify for your own creations.
encountertext = "Nidhogg_Warrior is\rrunning frantically"
nextwaves = {"bullettest_chaserorb"}
wavetimer = 5.0
arenasize = {155, 130}
music = "musAncientGuardian"
enemies = {"poseur"}
require("Monsters.poseur")
enemypositions = {{0, 0}}
-- A custom list with attacks to choose from.
-- Actual selection happens in EnemyDialogueEnding().
-- Put here in case you want to use it.
possible_attacks = {"bullettest_bouncy", "bullettest_chaserorb", "bullettest_touhou"}
function EncounterStarting()
-- If you want to change the game state immediately, this is the place.
Player.lv = 20
Player.hp = 99
Player.name = "Teemies"
poseur.GetVar("Berserk")
end
Thank you for reading.
The answer to my problem was to use SetGobal(), GetGobal().
For some reasons my previous attempt to simply use SetGobal()Resulted in nil value despite writing it like that SetGobal("Berserk",true) gave me a nill value error, as soon as I launch the game.
But I still used them wrong. First I needed to put it SetGobal() at the end of the condition instead of at the start of the the poseur.lua script because the change of value... for some reasons was being overwritten by it first installment.
And to test the variable in the function in my encounter.lua, I needed to write it like that
function EnemyDialogueStarting()
-- Good location for setting monster dialogue depending on how the battle is going.
if GetGlobal("Jimmies") == true then
TEEEST()
end
end
Also any tips an suggestions are still welcome !
Well firstly, in lua simple values like bool and number are copied on assignment:
global={}
a=2
global.a=a--this is a copy
a=4--this change won't affect value in table
print(global.a)--2
print(a)--4
Secondly,
SetGobal and the other mentioned functions are not part of lua language, they must be related to your engine. Probably, they use word 'Global' not as lua 'global' but in a sense defined by engine.
Depending on the engine specifics these functions might as well do a deep copy of any variable they're given (or might as well not work with complicated objects).

How do I make os.pullEvent not yield?

I'm trying to create a while true do loop, that reacts to clicks, using os.pullEvent, and also updates a monitor.
Problem being, it only updates the screen when I press one of the on screen buttons, and I've found out that's because pullEvent stops the script, until an event is fired.
Is it possible to make it so pullEvent doesn't stop me updating the monitor?
function getClick()
event,side,x,y = os.pullEvent("monitor_touch")
button.checkxy(x,y)
end
local tmp = 0;
while true do
button.label(2, 2, "Test "..tmp)
button.screen()
tmp++
getClick()
end
You can easily use the parallel api to run both codes essentially at the same time. How it works is it runs them in sequence until it hits something that uses os.pullEvent and then swaps over and does the other side, and if both stop at something that does os.pullEvent then it keeps swapping between until one yields and continues from there.
local function getClick()
local event,side,x,y = os.pullEvent("monitor_touch")
buttoncheckxy(x,y)
end
local tmp = 0
local function makeButtons()
while true do
button.label(2,2,"Test "..tmp)
button.screen()
tmp++
sleep(0)
end
end
parallel.waitForAny(getClick,makeButtons)
Now if you notice, first thing, I've made your while loop into a function and added a sleep inside it, so that it yields and allows the program to swap. At the end you see parallel.waitForAny() which runs the two functions that are specified and when one of them finishes, which in this case whenever you click on a button, then it ends. Notice however inside the arguments that I'm not calling the functions, I'm just passing them.
I don't have computercraft handy right now or look up the functions but i know that you can use the function os.startTimer(t) that will cause an event in t seconds (I think it is seconds)
usage:
update_rate = 1
local _timer = os.startTimer(update_rate)
while true do
local event = os.pullEvent()
if event == _timer then
--updte_screen()
_timer = os.startTimer(update_rate)
elseif event == --some oter events you want to take action for
--action()
end
end
note: the code is not tested and I didn't use computercraft in quite a while so pleas correct me if i did a mistake.

Easiest way to make lua script wait/pause/sleep/block for a few seconds?

I cant figure out how to get lua to do any common timing tricks, such as
sleep - stop all action on thread
pause/wait - don't go on to the next
command, but allow other code in the
application to continue
block - don't go on to next command until the
current one returns
And I've read that a
while os.clock()<time_point do
--nothing
end
eats up CPU time.
Any suggestions? Is there an API call I'm missing?
UPDATE: I wrote this question a long time ago trying to get WOW Lua to replay actions on a schedule (i.e. stand, wait 1 sec, dance, wait 2 sec, sit. Without pauses, these happen almost all in the same quarter second.) As it turned out WOW had purposely disabled pretty much everything that allows doing action on a clock because it could break the game or enable bots. I figured to re-create a clock once it had been taken away, I'd have to do something crazy like create a work array (with an action and execution time) and then register an event handler on a bunch of common events, like mouse move, then in the even handler, process any action whose time had come. The event handler wouldn't actually happen every X milliseconds, but if it was happening every 2-100 ms, it would be close enough. Sadly I never tried it.
[I was going to post this as a comment on John Cromartie's post, but didn't realize you couldn't use formatting in a comment.]
I agree. Dropping it to a shell with os.execute() will definitely work but in general making shell calls is expensive. Wrapping some C code will be much quicker at run-time. In C/C++ on a Linux system, you could use:
static int lua_sleep(lua_State *L)
{
int m = static_cast<int> (luaL_checknumber(L,1));
usleep(m * 1000);
// usleep takes microseconds. This converts the parameter to milliseconds.
// Change this as necessary.
// Alternatively, use 'sleep()' to treat the parameter as whole seconds.
return 0;
}
Then, in main, do:
lua_pushcfunction(L, lua_sleep);
lua_setglobal(L, "sleep");
where "L" is your lua_State. Then, in your Lua script called from C/C++, you can use your function by calling:
sleep(1000) -- Sleeps for one second
If you happen to use LuaSocket in your project, or just have it installed and don't mind to use it, you can use the socket.sleep(time) function which sleeps for a given amount of time (in seconds).
This works both on Windows and Unix, and you do not have to compile additional modules.
I should add that the function supports fractional seconds as a parameter, i.e. socket.sleep(0.5) will sleep half a second. It uses Sleep() on Windows and nanosleep() elsewhere, so you may have issues with Windows accuracy when time gets too low.
You can't do it in pure Lua without eating CPU, but there's a simple, non-portable way:
os.execute("sleep 1")
(it will block)
Obviously, this only works on operating systems for which "sleep 1" is a valid command, for instance Unix, but not Windows.
Sleep Function - Usage : sleep(1) -- sleeps for 1 second
local clock = os.clock
function sleep(n) -- seconds
local t0 = clock()
while clock() - t0 <= n do
end
end
Pause Function - Usage : pause() -- pause and waits for the Return key
function pause()
io.stdin:read'*l'
end
hope, this is what you needed! :D - Joe DF
for windows you can do this:
os.execute("CHOICE /n /d:y /c:yn /t:5")
It doesn't get easier than this. Sleep might be implemented in your FLTK or whatever, but this covers all the best ways to do standard sort of system sleeps without special event interrupts. Behold:
-- we "pcall" (try/catch) the "ex", which had better include os.sleep
-- it may be a part of the standard library in future Lua versions (past 5.2)
local ok,ex = pcall(require,"ex")
if ok then
-- print("Ex")
-- we need a hack now too? ex.install(), you say? okay
pcall(ex.install)
-- let's try something else. why not?
if ex.sleep and not os.sleep then os.sleep = ex.sleep end
end
if not os.sleep then
-- we make os.sleep
-- first by trying ffi, which is part of LuaJIT, which lets us write C code
local ok,ffi = pcall(require,"ffi")
if ok then
-- print("FFI")
-- we can use FFI
-- let's just check one more time to make sure we still don't have os.sleep
if not os.sleep then
-- okay, here is our custom C sleep code:
ffi.cdef[[
void Sleep(int ms);
int poll(struct pollfd *fds,unsigned long nfds,int timeout);
]]
if ffi.os == "Windows" then
os.sleep = function(sec)
ffi.C.Sleep(sec*1000)
end
else
os.sleep = function(sec)
ffi.C.poll(nil,0,sec*1000)
end
end
end
else
-- if we can't use FFI, we try LuaSocket, which is just called "socket"
-- I'm 99.99999999% sure of that
local ok,socket = pcall(require,"socket")
-- ...but I'm not 100% sure of that
if not ok then local ok,socket = pcall(require,"luasocket") end
-- so if we're really using socket...
if ok then
-- print("Socket")
-- we might as well confirm there still is no os.sleep
if not os.sleep then
-- our custom socket.select to os.sleep code:
os.sleep = function(sec)
socket.select(nil,nil,sec)
end
end
else
-- now we're going to test "alien"
local ok,alien = pcall(require,"alien")
if ok then
-- print("Alien")
-- beam me up...
if not os.sleep then
-- if we still don't have os.sleep, that is
-- now, I don't know what the hell the following code does
if alien.platform == "windows" then
kernel32 = alien.load("kernel32.dll")
local slep = kernel32.Sleep
slep:types{ret="void",abi="stdcall","uint"}
os.sleep = function(sec)
slep(sec*1000)
end
else
local pol = alien.default.poll
pol:types('struct', 'unsigned long', 'int')
os.sleep = function(sec)
pol(nil,0,sec*1000)
end
end
end
elseif package.config:match("^\\") then
-- print("busywait")
-- if the computer is politically opposed to NIXon, we do the busywait
-- and shake it all about
os.sleep = function(sec)
local timr = os.time()
repeat until os.time() > timr + sec
end
else
-- print("NIX")
-- or we get NIXed
os.sleep = function(sec)
os.execute("sleep " .. sec)
end
end
end
end
end
For the second request, pause/wait, where you stop processing in Lua and continue to run your application, you need coroutines. You end up with some C code like this following:
Lthread=lua_newthread(L);
luaL_loadfile(Lthread, file);
while ((status=lua_resume(Lthread, 0) == LUA_YIELD) {
/* do some C code here */
}
and in Lua, you have the following:
function try_pause (func, param)
local rc=func(param)
while rc == false do
coroutine.yield()
rc=func(param)
end
end
function is_data_ready (data)
local rc=true
-- check if data is ready, update rc to false if not ready
return rc
end
try_pause(is_data_ready, data)
I would implement a simple function to wrap the host system's sleep function in C.
Pure Lua uses only what is in ANSI standard C. Luiz Figuereido's lposix module contains much of what you need to do more systemsy things.
require 'alien'
if alien.platform == "windows" then
kernel32 = alien.load("kernel32.dll")
sleep = kernel32.Sleep
sleep:types{ret="void",abi="stdcall","uint"}
else
-- untested !!!
libc = alien.default
local usleep = libc.usleep
usleep:types('int', 'uint')
sleep = function(ms)
while ms > 1000 do
usleep(1000)
ms = ms - 1000
end
usleep(1000 * ms)
end
end
print('hello')
sleep(500) -- sleep 500 ms
print('world')
I agree with John on wrapping the sleep function.
You could also use this wrapped sleep function to implement a pause function in lua (which would simply sleep then check to see if a certain condition has changed every so often). An alternative is to use hooks.
I'm not exactly sure what you mean with your third bulletpoint (don't commands usually complete before the next is executed?) but hooks may be able to help with this also.
See:
Question: How can I end a Lua thread cleanly?
for an example of using hooks.
You can use:
os.execute("sleep 1") -- I think you can do every command of CMD using os.execute("command")
or you can use:
function wait(waitTime)
timer = os.time()
repeat until os.time() > timer + waitTime
end
wait(YourNumberHere)
You want win.Sleep(milliseconds), methinks.
Yeah, you definitely don't want to do a busy-wait like you describe.
It's also easy to use Alien as a libc/msvcrt wrapper:
> luarocks install alien
Then from lua:
require 'alien'
if alien.platform == "windows" then
-- untested!!
libc = alien.load("msvcrt.dll")
else
libc = alien.default
end
usleep = libc.usleep
usleep:types('int', 'uint')
function sleep(ms)
while ms > 1000 do
usleep(1000)
ms = ms - 1000
end
usleep(1000 * ms)
end
print('hello')
sleep(500) -- sleep 500 ms
print('world')
Caveat lector: I haven't tried this on MSWindows; I don't even know if msvcrt has a usleep()
I started with Lua but, then I found that I wanted to see the results instead of just the good old command line flash. So i just added the following line to my file and hey presto, the standard:
please press any key to continue...
os.execute("PAUSE")
My example file is only a print and then a pause statment so I am sure you don't need that posted here.
I am not sure of the CPU implications of a running a process for a full script. However stopping the code mid flow in debugging could be useful.
I believe for windows you may use: os.execute("ping 1.1.1.1 /n 1 /w <time in milliseconds> >nul as a simple timer.
(remove the "<>" when inserting the time in milliseconds) (there is a space between the rest of the code and >nul)
cy = function()
local T = os.time()
coroutine.yield(coroutine.resume(coroutine.create(function()
end)))
return os.time()-T
end
sleep = function(time)
if not time or time == 0 then
time = cy()
end
local t = 0
repeat
local T = os.time()
coroutine.yield(coroutine.resume(coroutine.create(function() end)))
t = t + (os.time()-T)
until t >= time
end
You can do this:
function Sleep(seconds)
local endTime = os.time() + seconds
while os.time() < endTime do
end
end
print("This is printed first!")
Sleep(5)
print("This is printed 5 seconds later!")
You could try this:
function wait(seconds)
local start = os.time()
repeat until os.time() > start + seconds
end
wait(5) print("cargo. Cargo what? Cargo, storage.") -- waits 5 seconds and then prints
It worked for me in Lua CLI.
This should work:
os.execute("PAUSE")

Resources