GMOD Lua Player death causing game to crash - lua

I'm working on a FFA gamemode in Garry's mod where players have to survive each other and a nextbot NPC hunting them. My override of the GM:PlayerDeath adequately indicates when the current round should be over, however after SpawnGhoulInitial() is called, any deaths cause the game to crash. Im doing all this round stuff in my roundsystem_2.lua file:
-- GLOBAL VALUES
roundActive = false
roundCountdownStartTime = 0
roundStartTime = 0
phase = 1
countDownLength = 5
initialGhoulDelay = 10
spawnWepDelay = 15
ghoulMadnessDelay = 20
roundLength = 30
DEFINE_BASECLASS( "gamemode_base" )
-- vector list dictionary of nexbot spawn locations per map, starting with initial spawn location
nextbot_spawns = {gm_backrooms_classic = {Vector(-7413, 318, 93), Vector(-4270, 4079, 93), Vector(-3279, -4620, 93), Vector(-11092, -4632, 93), Vector(-11086, 4316, 93)}}
function GM:UpdateTimer(time)
net.Start("round_timer")
net.WriteInt(time, 10)
net.Broadcast()
end
-- function used to spawn the first ghoul of the round
function SpawnGhoulInitial()
local name = RandomizeNextbot()
local Ent = ents.Create(name)
if( !IsValid(Ent)) then
print("entity is not valid.")
return
end
Ent:SetPos(nextbot_spawns.gm_backrooms_classic[1])
Ent:Spawn()
print("Spawned ghoul ".. name)
PrintMessage(4, "The Ghoul has spawned. SURVIVE!")
end
-- function called to spawn the next four ghouls to sweep the map and kill off straggling players
function SpawnGhoulMadness()
local name = RandomizeNextbot()
local Ent = ents.Create(name)
if( !IsValid(Ent)) then
print("entity is not valid.")
return
end
for i=2, 5 do
Ent:SetPos(nextbot_spawns.gm_backrooms_classic[i])
Ent:Spawn()
print("Spawned ghoul madness ghoul number ".. tostring(i))
end
PrintMessage(4, "The Ghoul has summoned some friends. RUN!")
end
-- function called to spawn in weapons for the players
function SpawnWeps()
for k, v in pairs(player.GetAll()) do
if (v:Alive()) then
v:Give("weapon_crowbar", false)
v:Give("weapon_pistol", false)
end
end
PrintMessage(4, "You've found supplies. Kill other survivors!")
end
function CountAlivePlayers()
local players = player.GetAll()
local alive = 0
for i = 1, #players do
local player = players[i]
if (player:Alive()) then
alive = alive + 1
end
end
return alive
end
-- function called to give us a random nextbot to spawn in
function RandomizeNextbot()
local names = {"table of nextbot names here, not posting them for stackoverflow because its embarrassing"}
local random_nextbot = math.random(#names)
return names[random_nextbot]
end
-- function called under the conditions that the round has ended, will start the next round.
function GM:EndRound(winner)
-- clear the map, clear sounds, print winner on screen, allow the respawning
if (winner == nil) then
PrintMessage(4, "Nobody Wins!")
else
PrintMessage(4, winner:Nick().." Wins!")
end
game.CleanUpMap(false, {})
RunConsoleCommand("stopsound")
timer.Remove("RoundTimer")
timer.Create("roundEndTimer", 5, 1, function()
for k, v in pairs(player.GetAll()) do
if (v:Alive()) then
v:StripWeapons()
v:KillSilent()
end
end
net.Start("round_active")
net.WriteBool(false)
net.Broadcast()
roundActive = false
end)
end
function GM:StartCountDownTimer(repetitions)
self:UpdateTimer(repetitions)
timer.Create("CountdownTimer", 1, repetitions, function()
repetitions = repetitions - 1
self:UpdateTimer(repetitions)
end)
end
function GM:StartRoundTimer(repetitions)
self:UpdateTimer(repetitions)
timer.Create("RoundTimer", 1, repetitions, function()
repetitions = repetitions - 1
self:UpdateTimer(repetitions)
if (repetitions <= 0) then
self:EndRound(nil)
end
end)
end
--overriding playerdeath function for best death results
function GM:PlayerDeath(victim, inflictor, attacker)
BaseClass.PlayerDeath(self, victim, inflictor, attacker)
local players = player.GetAll()
local lastAlive = NULL
for i = 1, #players do
local p = players[i]
if (p:Alive()) then
lastAlive = p
end
end
if (CountAlivePlayers() <= 1 && roundActive) then
self:EndRound(lastAlive)
end
end
-- hook for starting the round on player spawn if there are enough players.
-- countdown for round start will restart if a new player spawns in during the countdown.
function GM:DoRoundSpawn()
--every time player joins reset the countdown
if (CountAlivePlayers() > 1) then
roundCountdownStartTime = CurTime()
self:StartCountDownTimer(countDownLength)
end
end
-- The main timing of the round. Checks against curtime to find when
-- specific events in the round need to happen.
function GM:Think()
-- if there is more than one player, and the time elapsed is a full 30 sec, start round
if (CountAlivePlayers() > 1 && roundActive == false) then
if ((roundCountdownStartTime + countDownLength) < CurTime()) then
roundStartTime = CurTime()
roundActive = true
phase = 1
net.Start("round_active")
net.WriteBool(true)
net.Broadcast()
timer.Remove("CountdownTimer")
self:StartRoundTimer(roundLength)
PrintMessage(4, "The Round has begun. Last player to survive wins!")
end
end
-- if the round is active, do this stuff
if (roundActive == true) then
-- if its been 10 sec since round start, spawn the ghoul
if ((roundStartTime + initialGhoulDelay) < CurTime() && phase == 1) then
SpawnGhoulInitial()
phase = 2
end
-- if its been 30 sec since round start, spawn in weapons for players
if((roundStartTime + spawnWepDelay) < CurTime() && phase == 2) then
SpawnWeps()
phase = 3
end
-- if its been 120 sec (2 minutes) since round start, begin the ghoul madness
if((roundStartTime + ghoulMadnessDelay) < CurTime() && phase == 3) then
SpawnGhoulMadness()
phase = 4
end
end
end
However, I do call my GM:DoRoundSpawn function in my init.lua file:
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")
include("shared.lua")
include("roundsystem_2.lua")
util.AddNetworkString("round_timer")
util.AddNetworkString("round_active")
roundActive = false
playerSpawns = {--table of spawnpoints, too much to paste for stackoverflow}
function GM:PlayerSpawn(ply)
ply:SetGravity(.85)
ply:SetMaxHealth(100)
ply:SetupHands()
ply:SetWalkSpeed(250)
ply:SetRunSpeed(450)
ply:SetModel("models/player/Kleiner.mdl")
local random_spawn = math.random(#playerSpawns)
ply:SetPos(playerSpawns[random_spawn])
--this section basically will make it so if someone spawns
--in while the round is active, they will stay dead until
--the round is no longer active
print("Player: " .. ply:GetName() .. " has spawned!")
if (roundActive == true) then
ply:KillSilent()
return
end
self:DoRoundSpawn()
end
function GM:PlayerDeathThink(ply)
if (roundActive == false) then
ply:Spawn()
return true
else
return false
end
end
I've combed through both of these files a couple times over and I cant for the life of me figure out why deaths don't break the game until after the nextbot spawn happens. Is there something here I'm missing?

Related

function/server.lua:9: attempt to compare number with nil

So i try to make my own script its basecly a script when a Faction wonts to attack a Enemy Base they can aktivate via command the ticket system and everytime one of one faction dies the faction get one minus ticket if all tickets are done its showing u faction has lost the battle and every weapons of the defeated team will be removed out the inventory but i got the error and im not sure what its mean ( i dont find the issue)
code:
local Tokens = Config.Tokens
local Jobs = Config.Jobs
local Cooldown = Config.Cooldown
local CurrentTime = GetGameTimer()
local Timer = nil
RegisterServerEvent("startAttack")
AddEventHandler("startAttack", function(job1, job2)
if Tokens[job1] > 0 and Tokens[job2] > 0 then
Tokens[job1] = Tokens[job1] - 1
Tokens[job2] = Tokens[job2] - 1
TriggerClientEvent("updateTokens", -1, Tokens)
end
if Tokens[job1] == 0 or Tokens[job2] == 0 then
for i, job in pairs(Jobs) do
if Tokens[job] == 0 then
for id, player in pairs(GetPlayers()) do
if GetPlayerIdentifier(player, 0) == job then
DropPlayer(player, "Your faction has lost the battle.")
end
end
end
end
if Timer == nil then
Timer = CurrentTime + Cooldown
end
end
end)
Citizen.CreateThread(function()
while true do
Citizen.Wait(1000)
if Timer ~= nil and CurrentTime > Timer then
Timer = nil
for i, job in pairs(Jobs) do
Tokens[job] = 25
end
TriggerClientEvent("updateTokens", -1, Tokens)
end
end
end)
like i said it should be a ticket system where u lost a ticket if one die

Function Triggers even when if statement is false

local Players = game:GetService("Players")
local TouchPart = workspace["Start"]
local target = game.Workspace.Level1
local function teleport()
TouchPart.Touched:Connect(function(touched)
if touched.Parent:FindFirstChild("Humanoid") then
local Player = Players:GetPlayerFromCharacter(touched.Parent)
if Player then
--Player.Character.HumanoidRootPart.CFrame = target.CFrame + Vector3.new(0, 5, 0)
print("Teleport")
end
end
end)
end
while true do
local zeit = game.Workspace.wall1.time.Value
zeit = zeit - 1
game.Workspace.wall1.time.Value = zeit
game.Workspace.wall1.SurfaceGui.Frame.TextLabel.Text = zeit
wait(1)
print(zeit)
if zeit == 0 then
teleport()
game.Workspace.wall1.time.Value = 20
end
end
The Function Teleport should only be called if zeit == 0. And print("Teleport") should only work when zeit hits 0 and the player is touching the part.
My Problem is even if zeit isnt 0 the function teleport() prints "Teleport" into the console when a player touches the part. Am i missing something there ?
Your issue is that you are connecting a Touched listener every time zeit reaches zero. This listener will fire from here on out, until you disconnect it. It also means that after two loops, it will print "teleport" twice, and after three you will see it printed three times with every touch.
You either need to disconnect the listener, or simply teleport the people touching the part when the timer strikes zero. Here's how you would do the latter :
local Players = game:GetService("Players")
local TouchPart = workspace["Start"]
local target = game.Workspace.Level1
local zeit = game.Workspace.wall1.time
local wallText = game.Workspace.wall1.SurfaceGui.Frame.TextLabel
-- function for teleporting all of the players currently touching the TouchPart
local function teleport()
local touchingPlayers = {}
local parts = TouchPart:GetTouchingParts()
for _, touched in ipairs(parts) do
local Player = Players:GetPlayerFromCharacter(touched.Parent)
if Player then
touchingPlayers[Player] = touched.Parent
end
end)
-- teleport all the touching players
for player, character in pairs(touchingPlayers) do
print("Teleporting " .. player.Name)
-- character:PivotTo(target.CFrame + Vector3.new(0, 5, 0))
end
end
-- keep the wall text constantly updated
zeit.Changed:Connect(function(newValue)
wallText.Text = tostring(newValue)
end)
-- update the counter every second
while wait(1) do
zeit.Value -= 1
if zeit.Value == 0 then
teleport()
wait(1)
zeit.Value = 20
end
end

[FIVEM]/LUA. How would i compare two player id's here

Ive been racking brains all day tryna fix this and i am new with lua and cant come up with a solution to this problem even though im rather close.
SO
I have a drift leader board in my server. The problem is the script doesnt check for the players [id] in the table. So if player A has a score of 10 and Player B has a score of 5. Player A can take the 2nd position on the leaderboard (table) by getting a score of any int between. so lets say 7.
LEADERBOARD_CLEAR_TIME = 15 -- THIS IS IN MINUTES
local Players = {}
local Leaderboards = {}
RegisterCommand('cleardriftscores', function(source)
if IsPlayerAceAllowed(source, 'drift') then --Ace permissions
clearLeaderboard()
end
end)
RegisterNetEvent("SaveScore")
AddEventHandler("SaveScore", function(client, data)
local identifier = (GetPlayerIdentifier(source, 0))
local playerName = GetPlayerName(source)
if Players[identifier] ~= nil then
if Players[identifier].pb < data.curScore then
-- Personal Best Beat
local oldScore = Players[identifier].pb
Players[identifier] = { pb = data.curScore }
chatMessage(source, string.format("Congrats! You have just beaten your personal best drift chain score of ^2%s^0 with ^2%s^0!", oldScore, data.curScore))
end
else
Players[identifier] = { pb = data.curScore }
end
if #Leaderboards == 0 then
table.insert(Leaderboards, {score = data.curScore, name = playerName, id = identifier})
chatMessage(-1, string.format("^2%s^0 has started off the leaderboard with a score of ^2%s^0!", playerName, data.curScore))
end
for k, v in ipairs(Leaderboards) do
if v.score < data.curScore and checkLeaderboard(identifier) then
-- Outpassed Someone
table.insert(Leaderboards, { score = data.curScore, name = playerName, id = identifier } )
chatMessage(-1, string.format("^2%s^0 has beaten ^2%s's^0 score of ^2%s^0 with ^2%s^0! They are in ^2%s^0 place", playerName, v.name, v.score, data.curScore, GetPlacement(k)))
break
end
end
table.sort(Leaderboards, compare) --Currently bugged, when player 1 is in first and player 2 is in second. Player 1 can take second by getting a score inbetween table entry 1 & 2
end)
function chatMessage(target, msg)
TriggerClientEvent('chat:addMessage', target or -1, {
color = { 255, 0, 0},
multiline = true,
args = {"[Drift] ", msg}
})
end
function checkLeaderboard(identifier)
for k, v in ipairs(Leaderboards) do
if v.id == identifier then
Leaderboards[k] = nil
end
end
return true
end
function compare(a, b) --actual compare funtion. Need to check for player id's somehow...
if a ~= nil and b ~= nil then
return a.score > b.score --I tried adding another compare here but that didnt work.
end
end
function GetPlacement(number) --This doesnt work very well. (12nd place??)
lastDigit = number % 10
local placement = 'th'
if lastDigit == 1 then
placement = 'st'
elseif lastDigit == 2 then
placement = 'nd'
elseif lastDigit == 3 then
placement = 'rd'
end
return number .. placement
end
function clearLeaderboard()
Leaderboards = {}
chatMessage(-1, "The Drift leaderboard has been cleared!")
end
Citizen.CreateThread(function() --Code for timer to reset leaderboard
while true do
while #Leaderboards == 0 do
Citizen.Wait(0)
end
Citizen.Wait((LEADERBOARD_CLEAR_TIME * 1000 * 60) - (60 * 1000 * 5))
chatMessage(-1, "The Drift Leaderboard is clearing in 5 minutes!")
Citizen.Wait(1000 * 60 * 3)
chatMessage(-1, "The Drift Leaderboard is clearing in 2 minutes!")
Citizen.Wait(1000 * 60 * 1)
chatMessage(-1, "The Drift Leaderboard is clearing in 1 minute!")
Citizen.Wait(1000 * 60 * 1)
clearLeaderboard()
end
end)
The problem here is the code that adds to the leaderboards.
If a player is only allowed to be on a leaderboard once, you should check for that!
Here's how I'd do it:
If the player has beaten his/her personal best then attempt to add his new score to the leaderboards.
Start from the lowest score on the leaderboard. For each score:
If the score owner is equal to the player, delete this score (it's outdated).
If the personal best is smaller than the currently inspected score, insert the personal best one below the current inspected score.
Doing so there won't be any duplicates.
Btw, compare function is just fine ;)
But, just a friendly reminder:
a ~= nil and b ~= nil
--This is simpler and faster!
a and b
Remember that nil in Lua evaluates to false. Therefore, if you need to check if something holds value just:
if a then --This checks if a isn't false or nil
--your code goes here
end
And also that line is not really useful in your comparing function.
Hope that helps!

If all the players were killed or resetting during a round, how do you stop the game and display a message for everybody?

I am developing a remastered game, and want to figure how the round will stop if all players were killed or reset, and display a message for all players, "Game over", and returns back to the lobby intermission.
This is my main script for the game I am making. In the past, I've tried If statements in this script, however it didn't work and it ends up not displaying my another scripts as well.
local s = script.Stat
t = 0
while true do
local plrs = game.Players:GetPlayers()
t = 15
repeat
t = t - 1
s.Value = t.." seconds left"
wait(1)
if plrs == 0 then
s.Value = "Alive: "..plrs
end
until t == 0
s.Value = "Game over!"
wait(5)
end
I expect the script to find all the players in a round that is killed, but the output seems to work only for the seconds remaining.
If you add a listener to every player's Humanoid.Died signal, you can keep track of which players die in a round.
In a Script, write something like this :
-- use a map of player names to keep track of who is still alive
local playersAlive = {}
local listenerTokens = {}
local SPAWN_LOCATION = CFrame.new()
-- make some helper functions
local function getCountPlayersAlive()
local numPlayersAlive = 0
for playerName, isAlive in pairs(playersAlive) do
numPlayersAlive = numPlayersAlive + 1
end
return numPlayersAlive
end
local function beginRound()
-- get the list of all of the players in the round
local allPlayers = game.Players:GetChildren()
for _, player in ipairs(allPlayers) do
-- find the associated Player in the workspace
local character = game.Workspace:FindFirstChild(player.Name)
if character then
-- add an event listener for when they die
local humanoid = character:FindFirstChildOfClass("Humanoid")
local deathSignalToken = humanoid.Died:Connect(function()
-- when this player dies, signal that there is one less player
playersAlive[player.Name] = nil
end)
-- add the player to the list of players for this round
playersAlive[player.Name] = true
-- move the players into the game arena
player.Character:SetPrimaryPartCFrame(SPAWN_LOCATION)
-- disconnect this signal at the end of the round
table.insert(listenerTokens, deathSignalToken)
else
-- could not find the player, make them sit out this round
end
end
end
local function endRound()
-- clean up the game board, reload the players, do whatever you have to
-- clean up any death signals from last round
for _, token in ipairs(listenerTokens) do
token:Disconnect()
end
end
-- set up the game loop
local GAME_LENGTH = 30 -- seconds
local INTERMISSION_LENGTH = 15 -- seconds
spawn(function()
while true do
-- phase 1 - set up the round
beginRound()
-- phase 2 - play the round
local gameTimer = GAME_LENGTH
while gameTimer > 0 and getCountPlayersAlive() > 0 do
local count = getCountPlayersAlive()
print(string.format("Time Left : %d - Players Left : %d", gameTimer, count))
wait(1)
gameTimer = gameTimer - 1
end
-- phase 3 - celebrate winners and clean up the previous round
endRound()
-- phase 4 - intermission before the next round
wait(INTERMISSION_LENGTH)
end
end)
Sorry for the lengthy example, but I hope this helps.

Error in code. Lua on NodeMCU

I have a motorized roller blind project.
I am following this instructable: https://www.instructables.com/id/Motorized-WiFi-IKEA-Roller-Blind/ .
I am using the code in the instructable, but I suspect tha fault is in one of these codes:
config.lua
-- file : config.lua
local module = {}
module.SSID = {}
module.SSID["ssid"] = "password"
-- example for local MQTT
--module.MQHOST = "ohab.local"
--module.MQPORT = 1883
--module.MQID = node.chipid()
--module.MQUSR = ""
--module.MQPW = ""
-- example for cloud MQTT
module.MQHOST = "192.***.*.*"
module.MQPORT = 1883
module.MQID = node.chipid()
module.MQUSR = "username"
module.MQPW = "password"
module.MQTLS = 1 -- 0 = unsecured, 1 = TLS/SSL
module.ENDPOINT = "/house/masterbedroom/rollerblind/"
module.ID = "0"
--module.SUB = "set"
module.SUB = {[module.ENDPOINT .. module.ID .. "/set"]=0,[module.ENDPOINT .. "all"]=0}
module.POST = module.ENDPOINT .. module.ID .. "/status"
return module
wifi_setup
-- file: setup.lua
local module = {}
local function wifi_wait_ip()
if wifi.sta.getip()== nil then
print("IP unavailable, Waiting...")
else
tmr.stop(1)
gpio.write(pin_led,1) --off
print("\n================== ==================")
print("ESP8266 mode is: " .. wifi.getmode())
print("MAC address is: " .. wifi.ap.getmac())
print("IP is "..wifi.sta.getip())
print("====================================")
mq.start()
end
end
local function wifi_start(list_aps)
if list_aps then
gpio.write(pin_led,0) --on
for key,value in pairs(list_aps) do
if config.SSID and config.SSID[key] then
wifi.setmode(wifi.STATION);
wifi.sta.config(key,config.SSID[key])
wifi.sta.connect()
print("Connecting to " .. key .. " ...")
--config.SSID = nil -- can save memory
tmr.alarm(1, 2500, 1, wifi_wait_ip)
end
end
else
print("Error getting AP list")
end
end
function module.start()
print("Configuring Wifi ...")
wifi.setmode(wifi.STATION);
wifi.sta.getap(wifi_start)
end
return module
Sadly I cant manage to go beyond step 4. After I have tried to download the code to the ESP8266 I only get this error:
PANIC: unprotected error in call to Lua API (wifi_setup.lua:25: bad argument #1 to 'config' (config table not found!))
I have only changed the stuff the instructable told me to change, and I have tried to look for faults myself but I cant find any... It's the first time I am using Lua, so this is totally new for me.
Hope somebody here can offer some help. Been stuck on this for days...
THIS PART OF THE PROBLEM IS SOLVED. CHECK COMMENTS FOR SOLUTION
Sadly when one problem is solved, a new one rises...
The chip is now successfully connecting to the wifi and mqtt service, but when I try to do the dry run by putting in the command 'step_move(1000,FWD,2)' nothing happens. The motor should rotate...
Also when I press the button I get a new panic error as follows:
PANIC: unprotected error in call to Lua API (button.lua:23: attempt to perform arithmetic on upvalue '?' (a nil value))
button.lua
--file button4.lua
do
-- use pin 1 as the input pulse width counter
local pin=5
local debounce = 150 --ms
local longpress = 2000 --ms
local pulse1, pulse2, du, now, trig = 1, 0, 0, tmr.now, gpio.trig
local prev_int_time, int_time, up_time = 0
local cal_steps = 100000
local cal_steps_dn = 0
local cal_steps_up = 0
local cal_state = 0 -- 0 = not calibration, 1 = calibrating down, 2 = calibrating up
state = 0 -- state: 0 = up, 1 = transition, 2 = down
gpio.mode(pin,gpio.INT)
local function pin4cb(level)
int_time = now() / 1000
if ((int_time - prev_int_time) > debounce) then
if (level == 0) then
up_time = int_time
else
if((int_time - up_time) > longpress) then
print("calibrating")
cal_state = 1
--cur_step = 100000
step_move(cal_steps,FWD,2)
else -- short press
print("short", cal_state)
if (cal_state == 2) then -- calibrated up (done)
print("calibration done")
state = 0 -- up
cur_step = 0
tot_steps = cal_steps - step_stepsleft
print("cal_steps: " .. cal_steps)
print("step_stepsleft: " .. step_stepsleft)
print("tot_steps: " .. tot_steps)
step_stop()
pins_disable()
cal_state = 0
if file.open("cfg_tot_steps.lua", "w+") then
file.write("tot_steps=" .. tot_steps .. '\n')
file.close()
end
elseif (cal_state == 1) then -- calibrated dn (switch direction)
print("calibration low point")
print(cal_steps - step_stepsleft)
step_stop()
step_move(cal_steps,REV,2)
cal_state = 2
elseif (cal_state == 0) then
if (state == 0 and step_stepsleft == 0) then -- i am up, go dowm
rollerblind.down()
-- state = 2
elseif (state == 1) then -- i am moving, do nothing
-- do nothing
elseif (state == 2 and step_stepsleft == 0) then -- i am down, go up
rollerblind.up()
-- state = 0
end
end
end
end
--print (level)
prev_int_time = int_time
end
end
gpio.trig(pin, "both", pin4cb)
end
Here is the code for the stepper.lua:
-- stepper.lua
-- code from: http://www.esp8266.com/viewtopic.php?f=19&t=2326
-- simple stepper driver for controlling a stepper motor with a
-- l293d driver
-- nodemcu pins: 0 5 6 7
stepper_pins = {1,3,2,4} -- (A-)blue, (A+)pink, (B-)yellow, (B+)orange
--stepper_pins = {1,2,3,4}
-- half or full stepping
step_states4 = {
{1,0,0,1},
{1,1,0,0},
{0,1,1,0},
{0,0,1,1}
}
step_states8 = {
{1,0,0,0},
{1,1,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,1,0},
{0,0,1,1},
{0,0,0,1},
{1,0,0,1},
}
step_states = step_states4 -- choose stepping mode
step_numstates = 4 -- change to match number of rows in step_states
step_delay = 10 -- choose speed
step_state = 0 -- updated by step_take-function
step_direction = 1 -- choose step direction -1, 1
step_stepsleft = 0 -- number of steps to move, will de decremented
step_timerid = 4 -- which timer to use for the steps
status_timerid = 2 -- timer id for posing of status messages
-- setup pins
function pins_enable()
for i = 1, 4, 1 do
gpio.mode(stepper_pins[i],gpio.OUTPUT)
end
end
function pins_disable()
-- for i = 1, 4, 1 do -- no power, all pins
for i = 2, 4, 1 do -- no power, all pins except one (to keep it in place)
gpio.mode(stepper_pins[i],gpio.INPUT)
end
end
-- turn off all pins to let motor rest
function step_stopstate()
for i = 1, 4, 1 do
gpio.write(stepper_pins[i], 0)
end
end
-- make stepper take one step
function step_take()
-- jump to the next state in the direction, wrap
step_state = step_state + step_direction
cur_step = cur_step + step_direction * FWD
if step_state > step_numstates then
step_state = 1;
elseif step_state < 1 then
step_state = step_numstates
end
-- write the current state to the pins
pins_enable()
for i = 1, 4, 1 do
gpio.write(stepper_pins[i], step_states[step_state][i])
end
-- might take another step after step_delay
step_stepsleft = step_stepsleft-1
if step_stepsleft > 0 then
-- if cur_step > 0 and cur_step < tot_steps and step_stepsleft > 0 then
tmr.alarm(step_timerid, 10, 0, step_take )
--tmr.alarm(step_timerid, 10, 0, step_take )
else
step_stopstate()
step_stop()
pins_disable()
mq.post_status()
if file.open("cfg_cur_step.lua", "w+") then
file.write("cur_step=" .. cur_step .. '\n')
file.close()
end
end
end
-- public method to start moving number of 'int steps' in 'int direction'
function step_move(steps, direction, delay)
tmr.stop(step_timerid)
step_stepsleft = steps
step_direction = direction
step_delay = delay
step_take()
end
function step_go_to(step, delay)
if step >= cur_step then
steps = step - cur_step
step_move(steps, FWD, delay)
end
if step <= cur_step then
steps = cur_step - step
step_move(steps, REV, delay)
end
end
function percent_go_to(percent, delay)
if(percent >= 0 and percent <= 100) then
step_stop()
tmr.register(status_timerid, 1000, tmr.ALARM_AUTO, function () mq.post_status() end)
tmr.start(status_timerid)
step = percent * tot_steps / 100
step_go_to(step, delay)
end
end
-- public method to cancel moving
function step_stop()
tmr.stop(step_timerid)
tmr.stop(status_timerid)
step_stepsleft = 0
step_stopstate()
end
Let's parse the error message one by one:
unprotected error in call to Lua API (wifi_setup.lua:25: bad argument #1 to 'config' (config table not found!))
Unprotected error means that you did a normal function call, as opposed to a protected call (aka pcall), which is a function call where you expect errors to occur and want to provide means to handle them. Since you did not do a protected call, Lua does not know how to handle the error and terminates right away (which is not a bad thing).
wifi_setup.lua:25 tells you the file and line at which the error occured.
bad argument #1 to 'config' means that the problem is due to the first argument passed to a function named config.
config table not found! is the error message provided by the implementer of that function.
So in summary, there is a problem with the function call wifi.sta.config(key,config.SSID[key]) as it expects a table as first argument, while you provided something different (ie. a string with the BSSID). Indeed checking with the NodeMCU docs shows that you need to pass a table to this function:
wifi.sta.config()
Sets the WiFi station configuration. [...]
Syntax
wifi.sta.config(station_config)
Parameters
station_config table containing configuration data for
station
The expected layout of the table is documented in detail on that page as well.
ComicSansMS provided a great answer the essence of which is that you need to replace
wifi.sta.config(key,config.SSID[key])
with
wifi.sta.config{ssid=key,pwd=config.SSID[key]}
Thus a standalone example could be like this:
--wifi.setmode(wifi.NULLMODE)
config = {}
config.SSID = {}
config.SSID["ssid"] = "password"
function wifi_wait_ip()
if wifi.sta.getip() == nil then
print("IP unavailable, Waiting...")
else
tmr.stop(1)
print("\n====================================")
print("ESP8266 mode is: " .. wifi.getmode())
print("MAC address is: " .. wifi.ap.getmac())
print("IP is " .. wifi.sta.getip())
print("====================================")
end
end
function wifi_start(list_aps)
if list_aps then
for key, value in pairs(list_aps) do
if config.SSID and config.SSID[key] then
wifi.setmode(wifi.STATION);
wifi.sta.config{ssid=key, pwd=config.SSID[key]}
-- wifi.sta.connect() not needed as config() uses auto-connect=true by default
print("Connecting to " .. key .. " ...")
tmr.alarm(1, 2500, 1, wifi_wait_ip)
end
end
else
print("Error getting AP list")
end
end
function start()
print("Configuring Wifi ...")
wifi.setmode(wifi.STATION)
wifi.sta.getap(wifi_start)
end
start()

Resources