Noble BLE in Node.js - sometimes works, sometimes doesn't - javascript

I have the following code I'm trying to run and I'm getting different results every time I run it. Sometimes I just get "Powered Off". Sometimes I get that plus "Powered on". Sometimes I get all of the above plus "Scanning started". And sometimes it runs all the way thru and I get a bunch of "still searching..." and it eventually hits the BLE device and returns the advertisement.
I have no idea why this is working sometimes and not others.
var async = require('async');
var noble = require('../index');
var peripheralIdOrAddress = "8cf681a590f3";
noble.on('stateChange', function(state) {
if (state === 'poweredOn') {
noble.startScanning();
console.log("Powered On")
} else {
noble.stopScanning();
console.log("Powered Off")
}
});
noble.on('scanStart', ()=>console.log("Scanning started"))
noble.on('scanStop', ()=>console.log("Scanning stopped"))
noble.on('discover', function(peripheral) {
console.log("Discovered something!");
if (peripheral.id === peripheralIdOrAddress || peripheral.address === peripheralIdOrAddress)
{
noble.stopScanning();
console.log('peripheral with ID ' + peripheral.id + ' found');
var advertisement = peripheral.advertisement;
console.log(JSON.stringify(advertisement));
peripheral.on('disconnect', function() {
console.log("Peripheral disconnected");
process.exit(0);
});
peripheral.on('connect', ()=>console.log("Peripheral connected"));
peripheral.connect(function(error) {
peripheral.discoverServices("a0521000-2a63-479e-9d0a-09dfa7c8fd98",()=>console.log("Service discovered"));
});
}
else{console.log("Not the one. still searching...")}
});

Crazy addition to this question. I tried a lot of things with turning stuff off and unplugging things.
In the end I found out it was the power port. If I'm plugged into wall power, I get about 1 out of ever 10 runs that works. If I unplug wall power and go off battery, it works every time.
So bizarre.

Related

firebase realtime database transaction not working

I followed the tutorial pretty closely and am testing a "like" feature with a transaction. However, when I test it with 2 devices, the count doesn't seem to hold up well.
The error is when I click the like-button on both devices, there are times that the counter goes up by two, but that are times that the counter goes up then down making it increase by 0 (when in fact there were two like buttons). Similar problem when there is two un-liking buttons pressed at the same time.
Unliking both buttons (at the same time) could also cause the counters to increase by two instead... when it should be decreasing it by two.
var liked; // global variable to check if button has been liked
document.getElementById("like-button").onclick = function () {
console.log("click");
database.ref("brands/" + brand + "/" + dealId).transaction(function(post) {
console.log("post:" , post);
if (post) {
if (post.likes && liked) {
post.likes--;
liked = false;
}
else {
post.likes++;
liked = true;
}
}
return post;
});
}
Wondering what is the problem here given I followed this transaction pretty closely.
https://firebase.google.com/docs/database/web/read-and-write
Edit: JSON I have
Brand3
Brand3ID
impressions: 0
likes: 16
views: 0
Update: I noticed that the post logs 3 times when i click the button simultaneously on 2 devices - which could possibly explain the failure in handling the global flag, but am still unable to resolve why. Usually post should only log twice, one null and one when it detects the post (Firebase realtime database transaction handler gets called twice most of the time)
I believe I found the answer.
I've learnt that firebase transaction will run multiple times until successful (references: https://firebase.google.com/docs/database/web/read-and-write, Firebase transactions in NodeJS always running 3 times?), hence my liked flag was always changing depending on the number of times the transaction ran.
I fixed it by extracting the flag out so that it is not dependent on the number of transactions ran.
var liked; // global variable
document.getElementById("like-button").onclick = function () {
if (liked){
database.ref("brands/" + brand + "/" + dealId).transaction(function(post) {
if (post) {
if (post.likes) {
post.likes--;
}
}
console.log("post:" , post);
console.log("liked: ", liked);
return post;
});
liked = false;
}
else{ // not liked
database.ref("brands/" + brand + "/" + dealId).transaction(function(post) {
if (post) {
if (post.likes) {
post.likes++;
}
}
console.log("post:" , post);
console.log("liked: ", liked);
return post;
});
liked = true;
}
}
Feel free to let me know if there's a more elegant way.

discord.js spam command troubles

bot.on('message', message => {
if (message.content === 'spam') {
message.channel.send('spam');
while (message.channel.send('spam')) {
if (message.content === 'stop spam') {
return message.channel.send('stopped');
}
}
}
});
im still fairly new to javascript so im not sure if this is even possible the way ive been trying to do it ive looked through w3schools developers.mozilla and even a few questions that are already on here; ive tried using do while, and for loops and ive tried multiple versions of the code i have up there
the ultimate goal is if a user sends the word 'spam' the bot should continuously send the word 'spam' and keep doing so till the bot is turned off or a user sends the words 'stop spam'
Here are some things you should know about the code you're working with:
message.channel.send returns a Promise, so you can't put that in a while loop, because you need to have something that is true or false (a Boolean).
Right now you're trying to check if a message's content is equal to 'stop spam' while you're inside the if-statement checking if the content is equal to 'spam' - So you'll never get inside the inner if-statement.
I would recommend practicing basic javascript a bit more, then moving to Node.js, then coming back to Discord.js - However, it might be cool for you to see a spammer work, so I wrote a some spam code you could use - check this out:
First, create a new file named spamCtrl.js that looks like this (see comments in code for descriptions of what's going on):
let spamming = false;
let spamChannel = undefined;
// spam function repeats until variable spamming is false
function spam() {
return new Promise((resolve, reject) => {
// add check to make sure discord channel exists
if (!spamChannel)
reject('Channel is undefined!');
// send message on spam channel
spamChannel.send('spam')
.then(msg => {
// wait 100 ms until sending next spam message
setTimeout(() => {
// continue spamming if spamming variable is true
if (spamming) {
spam()
.then(resolve) // not entirely necessary, but good practice
.catch(console.log); // log error to console in case one shows up
}
// otherwise, just resolve promise to end this looping
else {
resolve();
}
}, 100)
})
.catch(console.log);
});
}
// public functions that will be used in your index.js file
module.exports = {
// pass in discord.js channel for spam function
setChannel: function(channel) {
spamChannel = channel;
},
// set spam status (true = start spamming, false = stop spamming)
setStatus: function (statusFlag) {
// get current status
let currentStatus = spamming;
// update spamming flag
spamming = statusFlag;
// if spamming should start, and it hasn't started already, call spam()
if (statusFlag && currentStatus != statusFlag) {
spam();
}
},
// not used in my commands, but you may find this useful somewhere
getStatus: function() {
return spamming;
}
};
Next, import that file into your index.js file (Must be in the same directory as your spamCtrl.js file - unless you change the require statement below).
// in index.js file, get controller for spam messages
let spamCtrl = require('./spamCtrl');
Final step: In your index.js file (or wherever you're handling your spam commands) set up your commands (this can be renamed as you like):
// 2 commands together make spamming work :)
case '?SPAM':
spamCtrl.setChannel(message.channel);
spamCtrl.setStatus(true);
break;
case '?STOP-SPAM':
spamCtrl.setStatus(false);
break;
Let me know if you would like any additional explanations on anything, or if you want to see some tweaks here 'n there.
Try using a variable instead. You cannot use message.channel.send('spam') for the while loop.
var spam = false;
if (message.content === 'spam') {
if (message.author.id !== bot.user.id) { // Replace bot with the instance of your bot Client.
spam = true;
} else {
if(spam) {
message.channel.send('spam');
}
}
if (message.content === 'stop spam') {
if(spam) {
message.channel.send('stopped');
}
spam = false;
}
}

opponent moves promise in multiplayer game

I have spent the last four days studying promises, coroutines, fibers, continuations, etc.
I am still unable to see how to resolve my multiplayer turn-based card game moves, in which the starting player is effectively the game 'controller' of up to five, either AI or human players.
The code below works but has one problem:-
it cannot detect human oppo's card moves and therefore continues playing without them, which, of course makes a mess.
Can anyone please suggest either a change to my overall concept or a way to use promise or one of any other 'synchronising' constructs?
Here are the four key areas of my code:
function oppoPlays () {
// can only go through here if game starter
if (joiner!="") {return;}
for (pp=1; pp<numberofplayers; pp++) {
if (oppoType[pp] == "AI") {
// an AI player moves
.
.
} else {
// non-AI player
var yourTurnmsg="It's "+playerNames[pp]+"'s turn";
// tell human player that it's their turn
$("#text_message").val(yourTurnmsg).trigger(jQuery.Event('keypress', { keyCode: 13, which: 13 }));
// how to detect human oppo's card moved?
}
}
}
// chat functionality
$("#text_message").on("keypress", function(e) {
if (e.keyCode == 13){
payload = new Object();
payload.action = 'chat_text';
payload.chat_text = tmsg; // It's michael29's turn
payload.user_id = playerNames[pp];
payload.game_no = game_no;
socket.send(JSON.stringify(payload));
}
});
// socket gets oppo's response
function checkJson(res, sttr_id, game_no) {
if(res.action=="game_move"){
// find player
var pp=playerNames.indexOf(res.user_id);
cpos=res.cardno;
playCard_oppo(pp, cpos);
}
}
// turn an oppo's card face up and update scores
function playCard_oppo(pp, cardno) {
// and move it to the stack
topoc= parseInt($("#oppo_card" + cardno).css('top'));
leftoc=parseInt($("#oppo_card" + cardno).css('left'));
$("#oppo_card" + cardno).css({ top: topoc, left: leftoc, opacity: "50%" });
.
.
if (joiner=="") {
// tell oppoPlays fn that the card has moved
}
}
The game is similar to uno in concept but with a scoring component
(aimed to help children with basic arithmetic).
Consider having a board state that is a global and player/AI moves modify it. Then, when it is time for the AI opponent to make a move, it consults the current board state and decides the move.
If your board state is just represented by elements on the page you'll need a way to scan it and calculate a useful in-memory representation of the board state. Without details of your implementation it's hard to be more specific.
Maybe start thinking in terms of a play-cycle, something like this :
(host) Broadcasts game state.
(host) Awaits confirmation from all clients that broadcast was received.
(clients) Render game state.
(host) Receives confirmations then informs next client (and hence its player) that it is his/her/its turn.
(host) Awaits player's move
(client) Unlocks UI allowing player to make move.
(player) Makes move.
(client) Sends move command and re-locks UI.
(host) Receives move command and modifies game state accordingly.
Then back to 1.
Also, think of AI players as just a special case of human players. If you can get it right for humans, then (re)introducing AI should be fairly simple.
The solution hinged around two things:-
separating the AI player code from the human player code;
adding and removing a window event that's triggered after a human's move is detected.
The condensed code now looks like this:-
// if this is game starter give each player a turn
if (joiner == "") {
// there's always at least one
pp = 1;
if (oppoType[pp] == "AI") { AIplays(); } else { humanPlays(); }
}
function humanPlays () {
// tell human player that it's their turn
var yourTurnmsg="It's "+playerNames[pp]+"'s turn"
$("#text_message").val(yourTurnmsg).trigger(jQuery.Event('keypress', { keyCode: 13, which: 13 }));
//window.addEventListener("humanPlayed", function(evnt) {
$(window).on("humanPlayed", function(evnt) {
endOfTurn();
});
}
function endOfTurn () {
if (!(winner)) {
if (pp++ != numberofplayers) {
if (oppoType[pp] == "AI") {
setTimeout(function (){ $("#clickForNextPlayer").show(); }, 1000);
} else {
$("#clickForNextPlayer").trigger('click');
}
}
}
}
// click for next player
$("#clickForNextPlayer").on('click', function() {
$("#clickForNextPlayer").hide();
$(window).off("humanPlayed");
if (pp == numberofplayers) {
// uncover outStack for game starter to play
$("#outStackcover").hide();
return;
}
if (oppoType[pp] == "AI") { AIplays(); } else { humanPlays(); }
});
function AIplays () {
AIcardno = chooseCard(pp, diffLevel);
.
.
if ($("#chatWindow").is(":visible")) {
payload = new Object();
payload.action="game_move";
payload.game_no=gamestarted;
payload.user_id=playerNames[pp];
payload.cardno=AIcardno;
socket.send(JSON.stringify(payload));
}
$("#oppo_card" + cc).css('background-image', "url(JerseyTeam" + playerNumbers[(pp == numberofplayers ? 1 : pp)] + ".gif)");
outStackturn();
endOfTurn();
}

Firebase RTDB `ref.once()` being executed multiple times

Playing with firebase RTDB lately, I've been using .once('value', v => ...) to build the GUI of my app. The exact code below:
<script>
function onAuthCompleted(usr) {
var salesTxRef = firebaseApp.database().ref('/salesTx').limitToLast(5);
salesTxRef.once("value", salesTx => {
salesTx.forEach(txRef => {
const tx = txRef.val();
const $item = $('<li></li>').html('$' + tx.total + ' <small>' + tx.currencyCode + '</small>');
$('.main ul').append($item);
});
});
}
</script>
The problem is that if I leave the page long enough opened, .once() gets called multiple times (once every 2-3 hours). Is this a bug on the javascript library? Known issue? Is there something that I'm incorrectly doing or a misunderstanding on my part?
As #Frank van Puffelen pointed out in the comment, the problem came from the method that called onAuthCompleted(usr):
firebaseApp.auth().onAuthStateChanged(function(user) {
if (user) {
if (typeof onAuthCompleted == 'function') {
onAuthCompleted(user);
}
} else {
console.log('User is not logged in. Cannot start session.');
}
}, function(error) {
console.log(error);
});
The onAuthStateChanged() being called hourly to refresh the session caused onAuthCompleted() to be called again thus registering the .once() method one more time (every ~hour). That was causing the strange perceived behavior.
I can confirm that .once() works as expected and it was my misunderstanding on how onAuthStateChange() works.
Thanks,

Serial port not working?

I made a program that sends out data to my arduino which detects what was sent and then turns on the correct pin according to what key is pressed.
When using the arduino software from my windows computer the arduino sketch works fine, I can make each pin turn on and off by sending either W A S Or D.
When sending via node the RX light on the arduino flashes but nothing else happens.
Can anyone help?
Node.js program:
var httpServer = require('http').createServer(function(req, response){ /* Serve your static files */ })
httpServer.listen(8080);
var nowjs = require("now");
var everyone = nowjs.initialize(httpServer);
everyone.now.logStuff = function(msg){
console.log(msg);
}
var SerialPort = require('serialport2').SerialPort;
var assert = require('assert');
var portName;
if (process.platform == 'win32') {
portName = 'COM4';
} else if (process.platform == 'darwin') {
portName = '/dev/cu.usbserial-A800eFN5';
} else {
portName = '/dev/ttyUSB0';
}
var readData = '';
var sp = new SerialPort();
sp.on('close', function (err) {
console.log('port closed');
});
sp.on('error', function (err) {
console.error("error", err);
});
sp.on('open', function () {
console.log('port opened... Press reset on the Arduino.');
});
sp.open(portName, {
baudRate: 9600,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false
});
everyone.now.forward = function() {
sp.write("w");
}
everyone.now.back = function() {
sp.write("s");
}
everyone.now.left = function() {
sp.write("a");
}
everyone.now.right = function() {
sp.write("d");
}
sp.on('data', function(data) {
console.log(data.toString());
});
Arduino Program:
void setup(){
Serial.begin(9600);
Serial.write("READY");
//Set all the pins we need to output pins
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
}
void loop (){
if (Serial.available() > 0) {
//read serial as a character
char ser = Serial.read();
Serial.write(ser);
//NOTE because the serial is read as "char" and not "int", the read value must be compared to character numbers
//hence the quotes around the numbers in the case statement
switch (ser) {
case 'w':
move(8);
break;
case 's':
move(9);
break;
case 'a':
move(10);
break;
case 'q':
move(10);
move(8);
break;
case 'd':
move(11);
break;
case 'e':
move(11);
move(8);
break;
}
}
}
void move(int pin){
Serial.print(pin);
digitalWrite(pin, HIGH);
delay(1);
digitalWrite(pin, LOW);
}
I recently dabbled into this. The Arduino automatically resets when it receives serial communication from most things other than the Arduino IDE. This is why you can send from the IDE but not node.js.
I have an Uno and put a capacitor between Reset and Ground.Here's a page with some good info on the subject.
Good luck. http://arduino.cc/playground/Main/DisablingAutoResetOnSerialConnection
I use node on a daily basis to send actions to my Arduino via usb or via bt and it works great in both cases.
I think your problem comes from sending letters. You should send a buffer instead, with the ascii value of the letter, just like that:
myPort.write(Buffer([myValueToBeSent]));
also, for this, I think you would be better with some "logic" interface, with data headers, number of actions, stuff like that. It is no required for you but it will make your code more robust and easier to modify in the future.
Here is an example of how I do it. First, Node:
var dataHeader = 0x0f, //beginning of the data stream, very useful if you intend to send a batch of actions
myFirstAction = 0x01,
mySecondAction = 0x02,
myThirdAction = 0x03;
You then call them like you did:
everyone.now.MyBatchOfActions = function() {
sp.write(Buffer([dataHeader]));
sp.write(Buffer([0x03])); // this is the number of actions for the Arduino code
sp.write(Buffer([myFirstAction]));
sp.write(Buffer([mySecondAction]));
sp.write(Buffer([myThirdAction]));
}
This way it is easy on the Arduino to Serial.read() the data: (Note that you need to define data header and data footer somewhere)
void readCommands(){
while(Serial.available() > 0){
// Read first byte of stream.
uint8_t numberOfActions;
uint8_t recievedByte = Serial.read();
// If first byte is equal to dataHeader, lets do
if(recievedByte == DATA_HEADER){
delay(10);
// Get the number of actions to execute
numberOfActions = Serial.read();
delay(10);
// Execute each actions
for (uint8_t i = 0 ; i < numberOfActions ; i++){
// Get action type
actionType = Serial.read();
if(actionType == 0x01){
// do you first action
}
else if(actionType == 0x02{
// do your second action
}
else if(actionType == 0x03){
// do your third action
}
}
}
}
}
I hope I'm clear and I hope it helps!
Cheers!
On the capacitor and reset issue...
There is a small capacitor between one of the serial control lines and reset on the Arduino in the later models. This capacitor causes the Arduino to reset when the port is opened but otherwise does not interfere with normal serial operation.
This reset trick allows the code upload to reset the Arduino as part of the upload process. When the Arduino starts up the code boot loader runs first for a short time before the loaded code runs.
The upload process is: Reset the Arduino which starts the boot loader, start the upload process in the Arduino IDE, establish communications, upload, then run the loaded code. When the Arduino starts up it waits for uploads for a short period of time, if none are received, it moves on to running the code.
I find this very useful as it allows us to effectively reset the Arduino just by closing and opening the port. In the old Arduino's, without this capacitor, you had to press the reset button at the right time to get the code to upload. And the timing was such that the Arduino spent much more time waiting before it started with the uploaded code.
In the problem described here, I do not believe he was having any troubles due to the reset trick used. It should have had only the effect of resetting the Arduino when he opened the serial port, and from the looks of his information, this is a desired side-effect.
In my case the issue was the reset, but that the serial port was opened - but not available for write until the reset has finished. Putting a 3s delay before writing to the port fixed the issue. Writing ASCII was not an issue.

Categories

Resources